RDS vs Aurora vs DynamoDB — Which AWS Database Should You Use?
"Should we use Aurora or DynamoDB?" I've heard this question in every architecture review I've attended. The answer is never simple, because the question is wrong. You don't pick a database engine first — you pick it based on your access patterns, consistency needs, and budget. Let's build a framework for making the right call.
The Decision Tree
Before comparing engines, answer these three questions:
-
Do you need relationships between data? (JOINs, foreign keys, transactions across tables)
- Yes: Go relational (RDS or Aurora)
- No: Consider DynamoDB
-
What's your read/write ratio?
- Read-heavy with complex queries: RDS/Aurora
- Write-heavy with simple key lookups: DynamoDB
-
How much operational overhead can you tolerate?
- Minimal: Aurora Serverless or DynamoDB
- Standard: RDS with Multi-AZ
RDS Engines Compared
RDS supports six database engines. Here's how they stack up:
| Engine | Version | Best For | License Cost | Notes |
|---|---|---|---|---|
| PostgreSQL | Up to 16.x | General purpose, GIS, JSON | Free | Most feature-rich open-source option |
| MySQL | Up to 8.x | Web applications, WordPress | Free | Largest community, most tutorials |
| MariaDB | Up to 10.x | MySQL alternative | Free | Better performance than MySQL in some cases |
| Oracle | 19c, 21c | Enterprise legacy apps | $$$ (or BYOL) | Only if you're already committed |
| SQL Server | 2019, 2022 | .NET applications | $$$ (included) | License cost is significant |
| Db2 | 11.5 | Mainframe migrations | $$$ (or BYOL) | Niche use case |
For new projects: PostgreSQL is the default choice. It's free, feature-rich, and AWS invests heavily in PostgreSQL-compatible Aurora.
# Create a PostgreSQL RDS instance
aws rds create-db-instance \
--db-instance-identifier my-app-db \
--db-instance-class db.t3.medium \
--engine postgres \
--engine-version 16.4 \
--master-username admin \
--master-user-password 'YourSecurePassword123!' \
--allocated-storage 100 \
--storage-type gp3 \
--vpc-security-group-ids sg-0abc123def456 \
--db-subnet-group-name my-db-subnet-group \
--multi-az \
--storage-encrypted \
--backup-retention-period 7 \
--deletion-protection \
--tags Key=Environment,Value=production
Aurora — The AWS Premium Option
Aurora is AWS's cloud-native database. It's compatible with MySQL and PostgreSQL but re-engineered from the ground up for the cloud. The key differences:
- 3x MySQL throughput, 5x PostgreSQL throughput (AWS's claim, real-world varies)
- Storage auto-scales from 10GB to 128TB with no downtime
- 6-way replication across 3 AZs automatically
- Up to 15 read replicas (RDS supports 5)
- Automatic failover in under 30 seconds
# Create an Aurora PostgreSQL cluster
aws rds create-db-cluster \
--db-cluster-identifier my-aurora-cluster \
--engine aurora-postgresql \
--engine-version 16.4 \
--master-username admin \
--master-user-password 'YourSecurePassword123!' \
--vpc-security-group-ids sg-0abc123def456 \
--db-subnet-group-name my-db-subnet-group \
--storage-encrypted \
--backup-retention-period 14 \
--deletion-protection
# Add a writer instance
aws rds create-db-instance \
--db-instance-identifier my-aurora-writer \
--db-cluster-identifier my-aurora-cluster \
--engine aurora-postgresql \
--db-instance-class db.r6g.large
# Add a reader instance
aws rds create-db-instance \
--db-instance-identifier my-aurora-reader \
--db-cluster-identifier my-aurora-cluster \
--engine aurora-postgresql \
--db-instance-class db.r6g.large
Aurora Serverless v2 is worth considering for variable workloads — it scales compute up and down automatically:
# Create an Aurora Serverless v2 cluster
aws rds create-db-cluster \
--db-cluster-identifier my-serverless-cluster \
--engine aurora-postgresql \
--engine-version 16.4 \
--master-username admin \
--master-user-password 'YourSecurePassword123!' \
--serverless-v2-scaling-configuration MinCapacity=0.5,MaxCapacity=16 \
--vpc-security-group-ids sg-0abc123def456 \
--db-subnet-group-name my-db-subnet-group
DynamoDB — When You Don't Need SQL
DynamoDB is a fully managed NoSQL database for key-value and document data. It shines when your access patterns are well-defined and you don't need complex queries.
# Create a DynamoDB table
aws dynamodb create-table \
--table-name UserSessions \
--attribute-definitions \
AttributeName=UserId,AttributeType=S \
AttributeName=SessionId,AttributeType=S \
--key-schema \
AttributeName=UserId,KeyType=HASH \
AttributeName=SessionId,KeyType=RANGE \
--billing-mode PAY_PER_REQUEST \
--tags Key=Environment,Value=production
# Put an item
aws dynamodb put-item \
--table-name UserSessions \
--item '{
"UserId": {"S": "user-123"},
"SessionId": {"S": "sess-abc"},
"LoginTime": {"S": "2025-04-19T10:30:00Z"},
"TTL": {"N": "1713614400"}
}'
# Query items for a user
aws dynamodb query \
--table-name UserSessions \
--key-condition-expression "UserId = :uid" \
--expression-attribute-values '{":uid": {"S": "user-123"}}' \
--output table
Read/Write Capacity Modes
| Mode | How It Works | Best For | Cost Model |
|---|---|---|---|
| On-Demand | Scales automatically, pay per request | Unpredictable traffic, new apps | $1.25 per million write, $0.25 per million read |
| Provisioned | You set read/write capacity units | Predictable traffic, cost optimization | ~70% cheaper than on-demand at steady state |
| Provisioned + Auto Scaling | Auto-adjusts within min/max bounds | Variable but patterned traffic | Best of both worlds |
Multi-AZ vs Read Replicas
These solve different problems:
| Feature | Multi-AZ | Read Replicas |
|---|---|---|
| Purpose | High availability (failover) | Read performance (scale reads) |
| Replication | Synchronous | Asynchronous |
| Failover | Automatic (~60 seconds) | Manual (promote to primary) |
| Read traffic | Standby not readable | Serves read queries |
| Cost | 2x instance cost | Per-replica instance cost |
| Cross-region | No (same region) | Yes |
# Enable Multi-AZ on an existing instance
aws rds modify-db-instance \
--db-instance-identifier my-app-db \
--multi-az \
--apply-immediately
# Create a read replica
aws rds create-db-instance-read-replica \
--db-instance-identifier my-app-db-read \
--source-db-instance-identifier my-app-db \
--db-instance-class db.t3.medium
Automated Backups
RDS and Aurora handle backups differently:
# Modify backup retention (RDS)
aws rds modify-db-instance \
--db-instance-identifier my-app-db \
--backup-retention-period 14 \
--preferred-backup-window "03:00-04:00"
# Create a manual snapshot (on-demand backup)
aws rds create-db-snapshot \
--db-instance-identifier my-app-db \
--db-snapshot-identifier my-app-db-$(date +%Y%m%d)
# Restore from a point in time
aws rds restore-db-instance-to-point-in-time \
--source-db-instance-identifier my-app-db \
--target-db-instance-identifier my-app-db-restored \
--restore-time "2025-04-19T10:00:00Z"
Cost Comparison (db.r6g.large, us-east-1)
| Database | Monthly Cost | Storage | What You Get |
|---|---|---|---|
| RDS PostgreSQL (Single-AZ) | ~$140 | $0.115/GB | Basic managed database |
| RDS PostgreSQL (Multi-AZ) | ~$280 | $0.23/GB | High availability failover |
| Aurora PostgreSQL | ~$190 | $0.10/GB (auto-scale) | Better performance, auto-scaling storage |
| Aurora Serverless v2 | ~$0.12/ACU-hour | $0.10/GB | Pay only when active |
| DynamoDB (On-Demand) | Pay per request | $0.25/GB | Zero management |
When to Use Each
- RDS: Standard web apps, existing SQL skills, predictable workloads, tight budgets
- Aurora: Production workloads needing high availability, read-heavy apps, growing data
- Aurora Serverless: Dev/test environments, infrequent but spiky workloads
- DynamoDB: Session stores, user profiles, IoT data, gaming leaderboards, sub-millisecond latency requirements
What's Next?
Databases store your data, but something needs to process it. Next up: AWS Lambda and serverless — how to build your first serverless application with zero infrastructure to manage.
This is Part 7 of our AWS series. Pick your database wisely — migrations are painful and expensive.
