Skip to content

AWS RDS

This guide provisions an AWS RDS for PostgreSQL instance for the Governance Platform. For why to use an external database, the required platform databases, and the shared Helm configuration, see the External Database overview.

For Aurora PostgreSQL the steps are equivalent - create a cluster instead of an instance and use the cluster writer endpoint as the host.

  • Kubernetes cluster with network reachability to RDS (VPC peering, a transit gateway, or the cluster and instance in the same VPC)
  • AWS CLI installed and configured with permissions to manage RDS and EC2 security groups
  • psql client available to create databases
  • kubectl access to the deployment namespace

Use the shared configuration from the External Database overview with these RDS-specific values:

values.yaml
postgresql:
enabled: false
global:
postgresql:
host: "<instance>.<region>.rds.amazonaws.com" # Filled in after step 2
port: 5432
database: "governance"
username: "postgres" # Master username from step 2
sslMode: "verify-full"
sslRootCert:
secretName: "postgres-ca" # Created in step 4
key: "ca.crt"

Set the master password from step 2 in the platform-database secret in secrets.yaml.

Create a DB subnet group and a security group that allows PostgreSQL traffic from the cluster:

Terminal window
# Set variables
export AWS_REGION="us-east-1"
export VPC_ID="<cluster-vpc-id>"
export CLUSTER_SG_ID="<cluster-node-security-group-id>"
# Create a DB subnet group from the VPC's private subnets
aws rds create-db-subnet-group \
--db-subnet-group-name governance-platform-db \
--db-subnet-group-description "Governance Platform database subnets" \
--subnet-ids <private-subnet-1> <private-subnet-2> \
--region $AWS_REGION
# Create a security group for the database
export DB_SG_ID=$(aws ec2 create-security-group \
--group-name governance-platform-db \
--description "Governance Platform database" \
--vpc-id $VPC_ID \
--query 'GroupId' --output text)
# Allow PostgreSQL (5432) from the cluster nodes only
aws ec2 authorize-security-group-ingress \
--group-id $DB_SG_ID \
--protocol tcp --port 5432 \
--source-group $CLUSTER_SG_ID
Terminal window
aws rds create-db-instance \
--db-instance-identifier governance-platform \
--engine postgres \
--engine-version 17.4 \
--db-instance-class db.t4g.medium \
--allocated-storage 50 \
--storage-type gp3 \
--storage-encrypted \
--master-username postgres \
--master-user-password "<master-password>" \
--db-subnet-group-name governance-platform-db \
--vpc-security-group-ids $DB_SG_ID \
--no-publicly-accessible \
--backup-retention-period 7 \
--region $AWS_REGION

Wait for the instance to become available and note its endpoint:

Terminal window
aws rds wait db-instance-available \
--db-instance-identifier governance-platform
aws rds describe-db-instances \
--db-instance-identifier governance-platform \
--query 'DBInstances[0].Endpoint.Address' --output text

The endpoint is the global.postgresql.host value.

Connect from a host that can reach the instance (for example, a bastion or a temporary pod in the cluster) and create both databases:

Terminal window
psql "host=<instance-endpoint> port=5432 user=postgres sslmode=require" <<'SQL'
CREATE DATABASE governance;
GRANT ALL PRIVILEGES ON DATABASE governance TO postgres;
CREATE DATABASE "IntegrityServiceDB";
GRANT ALL PRIVILEGES ON DATABASE "IntegrityServiceDB" TO postgres;
SQL

Required for sslMode: "verify-full" (recommended). Download the RDS certificate bundle and create a Kubernetes secret from it:

Terminal window
curl -sSo global-bundle.pem \
https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem
kubectl create secret generic postgres-ca \
--from-file=ca.crt=global-bundle.pem \
--namespace governance

If you skip this step, set sslMode: "require" and remove the sslRootCert block from values.yaml.

Fill in the Helm Configuration with the instance endpoint and master password, then proceed to the Helm Deployment guide. On the next helm upgrade --install, the bundled PostgreSQL is not deployed and all services connect to the RDS instance.

  1. Navigate to RDSCreate database
  2. Choose Standard createPostgreSQL, engine version 17.x
  3. Under Templates, choose Production (enables Multi-AZ and backups)
  4. Set the DB instance identifier, master username, and master password
  5. Under Connectivity, select the cluster’s VPC, choose No public access, and attach a security group that allows port 5432 from the cluster nodes
  6. Under Additional configuration, enable encryption and set the backup retention period
  7. Create the database, then follow steps 3-5 of the CLI method above

Follow the verification steps in the External Database overview, using the RDS endpoint as the host. The database list must include both governance and IntegrityServiceDB.

  • Keep the instance private - no public accessibility; restrict the security group to the cluster nodes
  • Use sslMode: "verify-full" with the RDS CA bundle to prevent man-in-the-middle attacks
  • Enable storage encryption at rest when creating the instance (it cannot be enabled later on RDS)
  • Enable deletion protection on the production instance (--deletion-protection)
  • Store the master password only in secrets.yaml or an approved secret manager - never in values.yaml
  • The instance class dominates cost; db.t4g.medium is a reasonable starting point and can be resized later
  • Multi-AZ roughly doubles instance cost but is recommended for production
  • Automated backup storage up to the size of the instance storage is included; snapshots beyond that are billed per GB
  • gp3 storage can scale IOPS independently of size if the platform’s write load grows