Skip to main content

Connecting to a Cloud Platform database

If you have an RDS instance on Cloud Platform and a namespace on a Container Platform cluster, you can use a port-forward pod on the Container Platform to reach that database and prove connectivity.

There are two parts to it:

  1. Allow your cluster’s address range through the database’s security group. This is a Terraform change in your Cloud Platform namespace.
  2. Connect to the database. To do this from your own machine, use a port-forward pod.

Before you start

You need:

Step 1: allow your cluster to reach the database

In your Cloud Platform namespace, add a security-group.tf file to the resources directory:

data "aws_vpc" "selected" {
  filter {
    name   = "tag:Name"
    values = [var.vpc_name == "live" ? "live-1" : var.vpc_name]
  }
}

resource "aws_security_group" "rds" {
  name        = "${var.namespace}-rds-sg"
  description = "Allow Container Platform access to this database"
  vpc_id      = data.aws_vpc.selected.id

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_security_group_rule" "rds_inbound" {
  type              = "ingress"
  description       = "Container Platform nonlive"
  from_port         = 5432
  to_port           = 5432
  protocol          = "tcp"
  security_group_id = aws_security_group.rds.id
  cidr_blocks       = ["10.195.48.0/20"] # your cluster, see the table below
}

You must change cidr_blocks to your Container Platform cluster:

Cluster Address range
container-platform-octo-nonlive 10.195.48.0/20
container-platform-hmpps-nonlive 10.195.64.0/20
container-platform-laa-nonlive 10.195.80.0/20
container-platform-cd-nonlive 10.195.96.0/20

These are the node ranges. Your pods have their own addresses, but traffic leaving the cluster appears to come from the node it is running on, so the node range is what the security group needs to allow.

More clusters will be added over time. The ranges for every cluster are defined in the vpc_cidr map in network/locals.tf, where primary is the node range you need and secondary is the pod range.

If your cluster is not listed above, use the primary value for it from that file. Ask in #container-platform-alpha-users if you are not sure which applies to you.

Then attach the security group to your RDS instance. In the file where you call the RDS module, add:

module "rds" {
  source = "github.com/ministryofjustice/cloud-platform-terraform-rds-instance?ref=<version>"

  vpc_name               = var.vpc_name
  vpc_security_group_ids = [aws_security_group.rds.id]

  # ...the rest of your configuration
}

Creating the security group is not enough on its own. If you do not add vpc_security_group_ids, the rule has no effect and your connection will time out.

Raise a pull request with both changes. Once it is merged, the pipeline applies it.

Use port 1433 for MS-SQL and 3306 for MySQL.

Step 2: run a port-forward pod

The remaining steps use two different clusters. Steps 2, 3 and 5 run against your Container Platform cluster. Step 4 reads a secret from your Cloud Platform namespace. Check which cluster you are pointed at before each step:

kubectl config current-context

Run this in your Container Platform namespace, replacing [your container platform namespace] and [your database hostname]. The hostname appears in both REMOTE_HOST and overrides sections. You can find the hostname in the rds_instance_address field of your RDS secret in your Cloud Platform namespace.

kubectl -n [your container platform namespace] run port-forward-pod \
  --image=ministryofjustice/port-forward \
  --port=5432 \
  --env="REMOTE_HOST=[your database hostname]" \
  --env="LOCAL_PORT=5432" \
  --env="REMOTE_PORT=5432" \
  --overrides='{"spec":{"securityContext":{"runAsNonRoot":true,"runAsUser":1001,"seccompProfile":{"type":"RuntimeDefault"}},"containers":[{"name":"port-forward-pod","image":"ministryofjustice/port-forward","ports":[{"containerPort":5432}],"env":[{"name":"REMOTE_HOST","value":"[your database hostname]"},{"name":"LOCAL_PORT","value":"5432"},{"name":"REMOTE_PORT","value":"5432"}],"securityContext":{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]}}}]}}'

This command differs from the equivalent one in the Cloud Platform user guide. Container Platform enforces the restricted Pod Security Standard, so the security settings have to be set explicitly. Without the --overrides the pod is rejected.

Check it started:

kubectl -n [your container platform namespace] logs port-forward-pod

You should see Socat started listening on 5432.

Step 3: forward traffic from your machine

kubectl -n [your container platform namespace] port-forward port-forward-pod 5432:5432

Leave this running while you use the database.

Step 4: connect

Get the credentials from the RDS secret in your Cloud Platform namespace:

kubectl -n [your cloud platform namespace] get secret [your rds secret] -o go-template='
dbname: {{index .data "database_name" | base64decode}}
username: {{index .data "database_username" | base64decode}}
password: {{index .data "database_password" | base64decode}}
'

Then connect as if the database were local:

psql --host localhost --port 5432 \
  --dbname [your database name] \
  --username [your database username] \
  --password

You will be prompted for the password.

Step 5: delete the port-forward pod

kubectl -n [your container platform namespace] delete pod port-forward-pod

Please delete it when you have finished. Pods created this way are not managed by a deployment and will be removed when the node they run on is replaced, so do not rely on one staying up.

Connecting from an application

The steps above are for reaching a database from your own machine.

A deployed application connects over the same network path, so no further network changes are needed. What it also needs is the database credentials, and those are held in your Cloud Platform namespace.

Do not commit credentials to a repository or put them in plain manifests. The Container Platform does not yet provide a mechanism for secrets management, and it is being tracked separately. If you need to connect an application to a database, talk to us first so we can deliberate on an approach in the #container-platform-alpha-users channel.

Troubleshooting

The connection times out.

Check the security group is attached to the instance, not just created. This is the most common cause. Confirm vpc_security_group_ids is set on your RDS module and that the change has been applied:

aws rds describe-db-instances --db-instance-identifier [your database identifier] --query 'DBInstances[0].VpcSecurityGroups[*].VpcSecurityGroupId' --output text
aws ec2 describe-security-groups --group-ids sg-xxxxxxxxxxxxxxxxx sg-yyyyyyyyyyyyyyyyy --output table

Then check you used the node range for your cluster from the table above, rather than a pod address.

The pod is rejected with a violates PodSecurity "restricted:latest" error.

You have run the command without the --overrides section. Use the full command in step 2.

socat fails to start, or the pod crashes.

LOCAL_PORT must be above 1024. The container does not run as root and cannot bind to privileged ports.

Getting help

Ask in #container-platform-alpha-users.

This page was last reviewed on 30 September 2026. It needs to be reviewed again on 30 March 2027 by the page owner #cloud-platform-notify .