Connect an application to RDS or Aurora securely

Get an application (Lambda, Fargate, EC2 or a laptop) connected to an RDS or Aurora database — network path and security groups, then credentials by IAM database authentication or Secrets Manager, then connection pooling with RDS Proxy — with TypeScript for the token and the secret, and the checks for each failure.

markdownguidev4EZT7-PVEXstage: buildtrust: officialservice: rdsinterface: sdkinterface: cliconnect to RDSAurora connectionRDS security grouppublicly accessibleIAM database authenticationgenerate-db-auth-tokenrds-signerrds-db:connectDbiResourceIdSecrets Manager database credentialsRDS Proxytoo many connectionsTLS RDS

Created 18 Oct 2026 · Modified 23 Oct 2026 · 4 versions

Get the network right first: the database's security group must admit the app's security group on the engine port. Then pick credentials: IAM database authentication (a 15-minute token, no stored password) or a Secrets Manager secret. Put RDS Proxy in between when many short-lived clients connect, as Lambda functions do.

1. Network path

  • App in the same VPC (the normal case): the database is not publicly accessible. Its security group allows the port (5432 PostgreSQL, 3306 MySQL) from the app's security group (reference/vpc/security-groups-and-nacls.md).
  • From outside the VPC: this requires Public accessibility = Yes, subnets routed to an internet gateway, and a security group rule for your IP. Prefer a bastion or Session Manager port forwarding instead (UNVERIFIED alternative; not in the collected RDS pages).
  • RDS doesn't answer ping. Test with nc -zv <endpoint> <port>. Addresses in 169.254.0.0/16 can't connect.
  • A new instance is unreachable while creating, which can take up to 20 minutes.

2a. IAM database authentication (MariaDB, MySQL, PostgreSQL)

  1. Enable IAM authentication on the instance or cluster.
  2. Create the database user: on PostgreSQL, CREATE USER app_user; GRANT rds_iam TO app_user;. With rds_iam, IAM auth takes precedence over a password. MySQL needs the AWS authentication plugin (UNVERIFIED syntax in the collected sources).
  3. Allow the caller rds-db:connect on arn:aws:rds-db:<region>:<account>:dbuser:<DbiResourceId>/app_user:
  • DbiResourceId (db-…) is the instance's resource ID. Find it with aws rds describe-db-instances --query "DBInstances[*].[DBInstanceIdentifier,DbiResourceId]".
  • Aurora uses the DbClusterResourceId.
  • Through RDS Proxy, use the proxy's ID (prx-…).
  1. Generate a token and use it as the password over TLS:
import { Signer } from "@aws-sdk/rds-signer";
import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager";

// Option A: IAM database authentication — a 15-minute token used as the password.
const signer = new Signer({
  hostname: "<instance-or-proxy endpoint>", // the RDS or RDS Proxy endpoint, not a custom DNS name
  port: 5432,
  username: "app_user",                     // a DB user granted rds_iam (PostgreSQL) or the AWS auth plugin (MySQL)
  region: "<region>",
});
const token = await signer.getAuthToken();
// pass { host, port, user: "app_user", password: token, ssl: { rejectUnauthorized: true } } to your driver

// Option B: credentials from Secrets Manager (cache them; don't call per query).
const sm = new SecretsManagerClient({ region: "<region>" });
const secret = await sm.send(new GetSecretValueCommand({ SecretId: "<secret name or ARN>" }));
const { username, password } = JSON.parse(secret.SecretString ?? "{}") as { username: string; password: string };
console.log(token.length > 0, username, password ? "***" : "");

Limits worth knowing:

  • Tokens last 15 minutes and are needed only at connect time: an open connection stays open.
  • Tokens are about 1 KB or more, so the driver mustn't truncate them.
  • You can't generate a token against a custom Route 53 name: use the RDS endpoint.
  • Some global condition keys (aws:SourceIp, aws:SourceVpc, aws:SourceVpce) aren't supported for rds-db:connect.
  • IAM auth uses 300–1000 MiB of extra database memory.
  • Token generation and IAM logins aren't logged in CloudTrail or CloudWatch.
  • PostgreSQL can't combine IAM auth with Kerberos.

2b. Secrets Manager

Store username and password in a secret. Grant the app secretsmanager:GetSecretValue on that secret, plus kms:Decrypt if it uses a customer managed key (reference/kms/key-policies-and-errors.md). Cache the value, and reconnect with a fresh read when authentication fails after a rotation. Turning rotation on (RDS-managed master passwords or a Lambda function) and its failure strings: guides/secrets-manager/rotate-secrets.md.

3. RDS Proxy (pooling)

Use it when many clients open short connections, such as Lambda bursts and serverless apps:

  • It pools and reuses connections, and queues or throttles new ones beyond its limits instead of overwhelming the database.
  • It keeps application connections open across failover to a standby.
  • Clients can be required to use IAM auth to the proxy. The proxy reaches the database with IAM auth or Secrets Manager secrets (up to 200).
  • The proxy is in the database's VPC and never public. It isn't supported with dedicated-tenancy VPCs or VPC Encryption Controls in enforce mode.
  • Pinning: statements over 16 KB, and some session-state changes, pin a client to one database connection, which reduces pooling.
  • Don't modify or delete the rdsproxyadmin database user. Parameter group changes need a database reboot before the proxy sees them.

Lambda-specific wiring: patterns/lambda-to-rds.md.

Check

nc -zv <endpoint> 5432
aws rds describe-db-instances --db-instance-identifier <id> \
  --query 'DBInstances[0].[DBInstanceStatus,PubliclyAccessible,VpcSecurityGroups,IAMDatabaseAuthenticationEnabled]'

Errors by message: reference/rds/errors.md.

Read this document as markdown — the same bytes an agent receives.