---
title: "AWS RDS"
description: "Configure AWS RDS for PostgreSQL for deployment"
source: "Guardian Platform"
---
## Overview

This guide provisions an AWS RDS for PostgreSQL instance for the Guardian Platform. For why to use an external database, the required platform databases, and the shared Helm configuration, see the [External Database overview](./external-database.mdx).

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

## Prerequisites

- 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

## Helm Configuration

Use the shared configuration from the [External Database overview](./external-database.mdx#helm-configuration) with these RDS-specific values:

```yaml title="values.yaml"
postgresql:
  enabled: false

global:
  postgresql:
    host: "<instance>.<id>.<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`. When the gateway stack is enabled, also set `gatewayDsn` there, as shown in the [External Database overview](./external-database.mdx#helm-configuration).

## Quick Start (CLI Method - Recommended)

Create the networking and instance with the AWS CLI, create the platform databases with `psql`, and mount the RDS CA bundle, in five steps.

### 1. Create Networking Resources

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

```bash
# 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 guardian-platform-db \
  --db-subnet-group-description "Guardian 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 guardian-platform-db \
  --description "Guardian Platform database" \
  --vpc-id $VPC_ID \
  --query 'GroupId' --output text \
  --region $AWS_REGION)

# 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 \
  --region $AWS_REGION
```

### 2. Create the RDS Instance

```bash
aws rds create-db-instance \
  --db-instance-identifier guardian-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 guardian-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:

```bash
aws rds wait db-instance-available \
  --db-instance-identifier guardian-platform \
  --region $AWS_REGION

aws rds describe-db-instances \
  --db-instance-identifier guardian-platform \
  --query 'DBInstances[0].Endpoint.Address' --output text \
  --region $AWS_REGION
```

The endpoint is the `global.postgresql.host` value.

### 3. Create the Platform Databases

Connect from a host that can reach the instance (for example, a bastion or a temporary pod in the cluster) and create the databases. Skip `guardian_gateway` unless the gateway stack is enabled:

```bash
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;

CREATE DATABASE guardian_gateway;
GRANT ALL PRIVILEGES ON DATABASE guardian_gateway TO postgres;
SQL
```

### 4. Mount the RDS CA Bundle

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

```bash
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 guardian
```

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

### 5. Update Chart Values

Fill in the [Helm Configuration](#helm-configuration) with the instance endpoint and master password, then proceed to the [Helm Deployment guide](./helm-deployment.mdx). On the next `helm upgrade --install`, the bundled PostgreSQL is not deployed and all services connect to the RDS instance.

## Alternative: Web UI Setup

The same setup can be done in the AWS Console instead of the CLI.

### Using the AWS Console

1. Navigate to **RDS** → **Create database**
2. Choose **Standard create** → **PostgreSQL**, 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

## Verification

Follow the [verification steps in the External Database overview](./external-database.mdx#verification), using the RDS endpoint as the host. The database list must include `governance` and `IntegrityServiceDB`, plus `guardian_gateway` when the gateway stack is enabled.

## Security Best Practices

- 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`

## Cost Considerations

- 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

## Official Documentation

- [Amazon RDS for PostgreSQL User Guide](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_PostgreSQL.html)
- [Using SSL with an RDS DB Instance](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL.html)
- [Amazon Aurora PostgreSQL](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Aurora.AuroraPostgreSQL.html)
- [Helm Chart Examples](https://github.com/eqtylab/deployment/tree/main/charts/guardian-platform/examples)

## Related

- [External Database](./external-database.mdx) — the shared configuration and verification for any managed PostgreSQL
- [Backup and Restore](./backup-and-restore.mdx#postgresql) — automated backups and pre-upgrade snapshots for RDS
- [AWS S3 Bucket](./aws-s3-bucket.mdx) — the AWS object storage the platform needs
- [AWS KMS](./aws-kms.mdx) — the AWS key provider for DID signing keys
- [Helm Deployment](./helm-deployment.mdx) — deploying the platform against the new instance