Oct 30, 20266 min read

How to Connect ECS Fargate to RDS PostgreSQL

The three ways to connect an ECS Fargate service to an RDS Postgres database: security groups with Secrets Manager, RDS Proxy, and IAM database authentication. Plus the setup where the database and its wiring come from your code.

Connecting an ECS Fargate service to Amazon RDS for PostgreSQL takes five pieces: the VPC and subnets, two security groups and the rule between them, the credentials in Secrets Manager, the task definition that injects them, and the IAM permission that lets ECS read the secret. Miss one and the task either never starts or hangs on a connection timeout.

This guide walks through the standard setup and the two common variations, RDS Proxy and IAM database authentication. It starts with Encore, where the database is declared in code and everything between Fargate and RDS is created for you.

Fargate to RDS options at a glance

EncoreSecurity groups and Secrets ManagerRDS ProxyIAM database auth
CredentialsPer-service roles, injected at runtimeSecret in the task definitionSecret held by the proxy15-minute tokens from the task role
Network setupCreated for youVPC, subnets, two security groupsPlus proxy security groupSame as the standard setup
Connection poolingPool per service instancePool per taskShared by the proxyPool per task
MigrationsRun on every deployYour pipelineYour pipelineYour pipeline
Local developmentencore run starts Postgres locallyYour own setupYour own setupYour own setup

Each option is covered below, starting with Encore.

Declaring the database and deploying to Fargate with Encore

Encore combines an open source infrastructure SDK for TypeScript and Go with a platform that provisions and deploys into your own AWS or GCP account. On AWS, it creates a VPC across two Availability Zones with separate subnets for the load balancer, compute and storage. Fargate tasks run in the private compute subnets, and RDS sits in private storage subnets that only accept connections from the compute subnets.

Application code passes through Encore and infrastructure guardrails to resources provisioned in your own AWS or GCP account.
Encore provisions the infrastructure your application needs in your own AWS or GCP account.

The database is part of the service:

// accounts/accounts.go package accounts import ( "context" "encore.dev/storage/sqldb" ) // RDS for PostgreSQL on AWS, a local Postgres under encore run. var db = sqldb.NewDatabase("accounts", sqldb.DatabaseConfig{ Migrations: "./migrations", }) type CreateParams struct { Email string `json:"email"` } type Account struct { ID int64 `json:"id"` Email string `json:"email"` } //encore:api public method=POST path=/accounts func Create(ctx context.Context, p *CreateParams) (*Account, error) { a := &Account{Email: p.Email} err := db.QueryRow(ctx, ` INSERT INTO accounts (email) VALUES ($1) ON CONFLICT (email) DO UPDATE SET email = EXCLUDED.email RETURNING id`, p.Email, ).Scan(&a.ID) return a, err }

The migration in accounts/migrations creates the accounts table the queries use:

-- accounts/migrations/1_create_accounts.up.sql CREATE TABLE accounts ( id BIGSERIAL PRIMARY KEY, email TEXT NOT NULL UNIQUE );
Get started
brew install encoredev/tap/encore &&
encore app create
Copy

Run in your terminal to get started locally.

encore run starts Postgres locally and applies the migration, so the endpoint can be tested with curl localhost:4000/accounts -d '{"email":"a@example.com"}' before anything is deployed.

On deploy, Encore creates the RDS instance if the environment does not have one, runs migrations through Emissary, a SOCKS proxy inside your VPC, and starts the new Fargate tasks. Encore creates the database users and injects the connection configuration into each service, and you can view the database user credentials in the Encore dashboard. Encore provisions an IAM role for the service. If you already run an RDS instance, Encore can import it instead.

An agent asked to "store accounts in Postgres" writes the migration and the query in one change and tests both locally.

Option 1: security groups and Secrets Manager

This is the standard setup from the ECS developer guide. Create the database in private subnets with a password that RDS manages in Secrets Manager, then allow the Fargate service's security group to reach it:

aws rds create-db-instance \ --db-instance-identifier accounts-db \ --engine postgres --db-instance-class db.t4g.micro \ --allocated-storage 20 \ --master-username app --manage-master-user-password \ --db-subnet-group-name private-db-subnets \ --vpc-security-group-ids sg-0db0000000000000 \ --no-publicly-accessible # Allow Postgres from the Fargate service's security group only. aws ec2 authorize-security-group-ingress \ --group-id sg-0db0000000000000 \ --protocol tcp --port 5432 \ --source-group sg-0app000000000000

In the task definition, the container reads the password from the secret RDS created. The :password:: suffix selects the JSON key inside the secret:

"secrets": [ { "name": "DB_PASSWORD", "valueFrom": "arn:aws:secretsmanager:eu-west-1:123456789012:secret:rds!db-abc123:password::" } ]

The task execution role needs secretsmanager:GetSecretValue on that secret, and kms:Decrypt if it uses a customer managed key. Without it, the task fails to start with a ResourceInitializationError. The app connects to the RDS endpoint with the username, password and sslmode=require, and the migrations need their own step in your pipeline, run from somewhere inside the VPC. In production, keep the master user for setup and migrations, and give the task its own PostgreSQL role with only the grants the application needs, stored in a separate secret.

Option 2: RDS Proxy

RDS Proxy sits between the tasks and the database, holds a pool of connections, and reads the credentials from Secrets Manager itself. It helps when many tasks scale out at once and would otherwise exhaust max_connections, and it shortens failover because clients keep talking to the proxy while it reconnects.

It adds a resource with its own security group, an IAM role for reading the secret, and an hourly cost per vCPU of the database. Some session-level features, such as prepared statements across transactions, can pin connections and reduce the pooling benefit.

Option 3: IAM database authentication

With IAM database authentication, the task role gets rds-db:connect for a database user, and the app generates a short-lived authentication token with the AWS SDK instead of using a password. Tokens are valid for 15 minutes, so the connection pool has to request a new one when opening connections.

It removes the long-lived password from the picture. It also adds token generation to your database client setup and a GRANT rds_iam on the Postgres user, and the network rules from Option 1 still apply.

Good to know

Each Fargate task holds its own pool, so total connections are the pool size times the task count. With autoscaling up to 20 tasks and a pool of 10, the database needs 200 connections, which a db.t4g.micro cannot provide. Size the pool, cap the task count, or add RDS Proxy.

How to choose

Pick Encore when you want RDS and Fargate created together in your own AWS account, with private networking, per-service database roles and migrations on every deploy, and the same code runnable locally.

Pick security groups and Secrets Manager for a single service talking to its own database. Add RDS Proxy when many tasks scale out quickly, and use IAM database authentication when your security policy rules out long-lived database passwords.

Frequently asked questions

How does an ECS Fargate task connect to RDS?

Put the RDS instance in private subnets of the same VPC, allow inbound port 5432 on the database security group from the Fargate service's security group, and pass the credentials to the task through the secrets section of the task definition, which reads them from Secrets Manager at startup. The app then connects to the RDS endpoint like any Postgres host.

Why can my Fargate task not connect to RDS?

The usual causes are a database security group that does not allow 5432 from the task's security group, the task running in a different VPC or in subnets without a route to the database, or a task execution role that lacks secretsmanager:GetSecretValue, which stops the task before your code runs.

Is there a way to connect Fargate to RDS without configuring security groups and secrets by hand?

Yes. With Encore, you declare the database in TypeScript or Go code and deploy to an AWS environment. Encore creates the VPC, the RDS instance in private subnets, the Fargate services with security groups allowing traffic to it, and a separate database role and credentials for each service, and runs migrations on every deploy.

Deploy to your cloud on AWS and Google Cloud
without managing infrastructure

Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.

~/orders — claude
Claude Code
Claude Code v2.1.180Opus 5.5~/orders