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
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 in169.254.0.0/16can't connect. - A new instance is unreachable while
creating, which can take up to 20 minutes.
2a. IAM database authentication (MariaDB, MySQL, PostgreSQL)
- Enable IAM authentication on the instance or cluster.
- Create the database user: on PostgreSQL,
CREATE USER app_user; GRANT rds_iam TO app_user;. Withrds_iam, IAM auth takes precedence over a password. MySQL needs the AWS authentication plugin (UNVERIFIED syntax in the collected sources). - Allow the caller
rds-db:connectonarn:aws:rds-db:<region>:<account>:dbuser:<DbiResourceId>/app_user:
- DbiResourceId (
db-…) is the instance's resource ID. Find it withaws rds describe-db-instances --query "DBInstances[*].[DBInstanceIdentifier,DbiResourceId]". - Aurora uses the DbClusterResourceId.
- Through RDS Proxy, use the proxy's ID (
prx-…).
- 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 forrds-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
rdsproxyadmindatabase 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.