AWS RDS

By Vishvesh Patel · DevOps EngineerUpdated September 12, 2026

Managed relational databases on AWS. Multi-AZ versus read replicas, backups and point-in-time recovery, the settings you cannot change later, and RDS Proxy.

Amazon RDS

RDS runs relational databases so you do not have to. Patching, backups, replication and failover are handled for you. What remains yours is schema, queries, connection management, and a handful of configuration decisions that cannot be changed after creation.

Those irreversible decisions are where most RDS regret comes from.

Engines

RDS supports MySQL, PostgreSQL, MariaDB, Oracle and SQL Server. Aurora is a separate AWS-built engine that is wire-compatible with MySQL and PostgreSQL, with a distributed storage layer giving faster failover, up to 15 read replicas and storage that scales automatically.

Choose standard RDS for straightforward workloads and full engine compatibility. Choose Aurora when you need its scaling and failover characteristics and can accept a different cost model, and note Aurora Serverless v2 scales capacity with load, which suits variable or unpredictable workloads.

Multi-AZ and read replicas solve different problems

These two are constantly confused, and the confusion causes both wasted money and missed outages.

Multi-AZRead replica
PurposeAvailabilityRead scaling
ReplicationSynchronousAsynchronous
Standby readableNo (classic instance deployment)Yes
FailoverAutomatic, ~60, 120sManual promotion
Cross-regionNoYes
Data loss on failoverNonePossible (replica may lag)

A Multi-AZ standby is not a read replica. In the classic instance deployment you cannot query it; it exists purely to take over. Teams sometimes enable Multi-AZ expecting read capacity and get none.

A read replica is not high availability. It is asynchronous, so promoting one after a primary failure can lose recent writes, and promotion is a manual decision.

Production databases usually want both: Multi-AZ for availability, plus read replicas if reads need scaling.

The newer Multi-AZ DB cluster deployment gives two readable standbys and faster failover, which narrows the distinction, but the classic deployment remains the default and behaves as above.

aws rds create-db-instance \
  --db-instance-identifier payments-prod \
  --db-instance-class db.m6g.large \
  --engine postgres --engine-version 16.4 \
  --allocated-storage 100 --storage-type gp3 \
  --multi-az \
  --storage-encrypted \
  --backup-retention-period 14 \
  --no-publicly-accessible \
  --deletion-protection

The settings you cannot change later

Get these right at creation:

  • Encryption at rest. Cannot be enabled on an existing instance. The migration path is snapshot, copy the snapshot with encryption, restore, which means downtime or a replication exercise. Always create encrypted; there is no cost to it.
  • VPC. An instance cannot move to a different VPC.
  • Engine. MySQL to PostgreSQL is a migration, not a setting.
  • Storage can only grow. Never shrink. Over-provisioning is a permanent cost.

Everything else, instance class, storage size, backup retention, parameter groups, can be changed, usually with a brief interruption or during a maintenance window.

Backups and recovery

Two separate mechanisms with different lifecycles:

Automated backups run daily and capture transaction logs, enabling point-in-time recovery to any second within the retention window. Retention is 0 to 35 days, and 0 disables backups entirely: which also disables PITR and read replicas.

Manual snapshots persist until you delete them, independent of retention or even of the instance.

aws rds restore-db-instance-to-point-in-time \
  --source-db-instance-identifier payments-prod \
  --target-db-instance-identifier payments-restored \
  --restore-time 2026-09-12T08:30:00Z

The detail that catches people during an incident: a restore creates a new instance with a new endpoint. It does not roll the existing database back. Recovery therefore means restoring, verifying, then repointing the application, which takes longer than people plan for and is worth rehearsing before you need it.

Deleting an instance offers a final snapshot. Take it. Combine with --deletion-protection on anything that matters, so deletion requires a deliberate two-step.

Connections, and why Lambda needs RDS Proxy

Relational databases have a hard connection limit, and each connection consumes memory. A db.t3.medium PostgreSQL instance supports a few hundred connections.

That works fine for a handful of application servers with connection pools. It fails badly with Lambda, where every concurrent execution opens its own connection: 500 concurrent invocations means 500 connection attempts, and the database refuses them.

RDS Proxy sits in front and pools connections:

aws rds create-db-proxy \
  --db-proxy-name payments-proxy \
  --engine-family POSTGRESQL \
  --auth '[{"AuthScheme":"SECRETS","SecretArn":"arn:aws:secretsmanager:...","IAMAuth":"REQUIRED"}]' \
  --role-arn arn:aws:iam::111122223333:role/rds-proxy-role \
  --vpc-subnet-ids subnet-0aaa subnet-0bbb

Besides pooling, it shortens failover time for clients and supports IAM authentication, which removes the database password from your application entirely.

Security

  • --no-publicly-accessible, always. A database in a private subnet, reachable only from your application's security group.
  • Reference security groups, not CIDRs: sg-database accepts 5432 from sg-application.
  • Store the password in Secrets Manager with automatic rotation, or use IAM database authentication so there is no password at all.
  • Enable encryption at creation, since you cannot add it later.
  • Enable enhanced monitoring and Performance Insights. Performance Insights is the tool that tells you which query is responsible when the database is slow, and the base tier is free.

Maintenance and upgrades

RDS applies patches during your maintenance window, and on Multi-AZ it patches the standby first, fails over, then patches the former primary, so expect a short failover rather than sustained downtime.

--auto-minor-version-upgrade is on by default and generally worth keeping. Major version upgrades are always deliberate: test against a restored snapshot first, because extension compatibility and query plan changes routinely produce surprises.

Every engine version reaches end of standard support, after which AWS forces an upgrade or moves you to paid extended support. Track it rather than being surprised.

Cost

  • Reserved Instances discount up to around 70% for steady databases, and a database is about the steadiest workload there is.
  • Graviton instance classes (db.m6g, db.r6g) are cheaper than equivalent Intel ones.
  • Storage only grows, so over-provisioning is permanent.
  • Multi-AZ roughly doubles instance cost. Worth it in production, rarely in development.
  • Snapshots are billed beyond the free allocation matched to your storage.
  • Stop non-production instances overnight, though note RDS restarts a stopped instance automatically after seven days.

Go deeper in this section

Sub-topicWhat you will learn
RDS SetupCreating an instance and connecting to it
RDS SecurityNetwork isolation, encryption and credentials

Quick reference

TaskCommand
List instancesaws rds describe-db-instances --query 'DBInstances[].DBInstanceIdentifier'
Endpointaws rds describe-db-instances --db-instance-identifier <id> --query 'DBInstances[0].Endpoint'
Manual snapshotaws rds create-db-snapshot --db-instance-identifier <id> --db-snapshot-identifier <name>
Point-in-time restoreaws rds restore-db-instance-to-point-in-time --source-db-instance-identifier <id> --restore-time <t>
Force a failover testaws rds reboot-db-instance --db-instance-identifier <id> --force-failover
Create a read replicaaws rds create-db-instance-read-replica --db-instance-identifier <new> --source-db-instance-identifier <src>
Recent eventsaws rds describe-events --source-identifier <id> --source-type db-instance

Key takeaways

  • Multi-AZ is availability; read replicas are read scaling. The Multi-AZ standby is not readable.
  • Encryption at rest cannot be added later. Always create encrypted.
  • Storage only grows: over-provisioning is a permanent cost.
  • A restore creates a new instance with a new endpoint. Rehearse recovery before you need it.
  • Backup retention of 0 disables PITR and read replicas, not just backups.
  • Lambda plus RDS needs RDS Proxy, or concurrent executions exhaust the connection limit.
  • Never make a database publicly accessible. Reference security groups rather than CIDR ranges.
  • Reserved Instances suit databases well: steady workloads with predictable capacity.

Check your understanding

Exercise

Multi-AZ for read capacity

A team enables Multi-AZ hoping to offload read queries to the standby. Reads do not get faster. Why?

Exercise

Database connections exhausted

A Lambda function querying RDS starts failing with "too many connections" during traffic spikes. The database has capacity to spare. What is the fix?


Frequently Asked Questions

What is the difference between Multi-AZ and a read replica?

Multi-AZ maintains a synchronous standby in another Availability Zone purely for availability. It is not readable in the classic instance deployment, and it takes over automatically in about 60 to 120 seconds if the primary fails, with no data loss. A read replica is an asynchronous copy that is readable, used to scale read traffic, and can live in another region. Because replication is asynchronous, promoting a replica after a failure can lose recent writes and is a manual decision. Production systems commonly use both.

Can I enable encryption on an existing RDS instance?

No. Encryption at rest must be enabled when the instance is created. To encrypt an existing database you take a snapshot, copy that snapshot specifying a KMS key, restore a new instance from the encrypted copy, and cut over, which means either downtime or a replication and switchover exercise. Since there is no additional charge for encryption, the sensible default is to enable it on every instance at creation and never face the migration.

How does point-in-time recovery work in RDS?

Automated backups capture a daily snapshot plus continuous transaction logs, allowing restoration to any second within the retention window of up to 35 days. The important detail is that a restore creates a new instance with a new endpoint rather than rewinding the existing database, so recovery involves restoring, verifying the data, then repointing your application. That takes longer than teams expect during an incident, which is why it is worth rehearsing. Setting backup retention to 0 disables this entirely, along with read replicas.

Why is my Lambda function exhausting RDS connections?

Because each concurrent Lambda execution runs in its own isolated environment and opens its own database connection. A spike to 500 concurrent invocations attempts 500 connections, and relational databases have a hard limit far below what a Lambda burst can produce. RDS Proxy solves this by pooling connections and multiplexing many clients onto few database connections, and it additionally shortens failover time and supports IAM authentication. Setting reserved concurrency on the function provides a secondary cap.

Should I use Aurora or standard RDS?

Standard RDS for straightforward workloads and full engine compatibility, including extensions or features Aurora does not support. Aurora when you need its distributed storage layer: faster failover, up to fifteen read replicas, storage that grows automatically, and better performance under heavy load. Aurora Serverless v2 scales capacity with demand, which suits variable or unpredictable workloads well. Aurora generally costs more for small steady databases and can cost less at scale, so compare against your actual usage rather than list prices.

How do I reduce RDS costs?

Buy Reserved Instances for production databases, which discount up to around 70% and suit a workload that is about as predictable as they come. Use Graviton instance classes such as db.m6g, which are cheaper than equivalent Intel classes. Right-size deliberately and remember storage can only grow, so over-provisioning is permanent. Disable Multi-AZ in non-production, since it roughly doubles instance cost. Stop development instances outside working hours, bearing in mind RDS restarts a stopped instance automatically after seven days.

Continue Learning

Explore Related Topics

When it breaks

Related Resources