1. Measure four weeks
| Metric | Over-provisioned if | Under-provisioned if |
|---|---|---|
CPUUtilization | p95 < 30% | Sustained > 80% |
FreeableMemory | Large and stable | Low, with swap or rising read latency |
ReadIOPS/WriteIOPS vs provisioned | Far below gp3/io baseline | At the limit, rising latency |
DatabaseConnections | n/a | Near max_connections |
| DB load (Performance Insights) vs vCPUs | Average active sessions ≪ vCPUs | AAS > vCPUs regularly |
Compute Optimizer also produces RDS/Aurora right-sizing recommendations. Treat them as a starting point.
2. Tune before downsizing or upsizing
Check the top SQL in Performance Insights first (see the RDS high CPU post). A missing index can be the difference between db.r6g.4xlarge and db.r6g.xlarge.
3. Current generation and Graviton
Moving from older classes (m5/r5) to Graviton classes (m7g/r7g, or current equivalents) usually gives better price-performance. Check engine version support for the class, then modify during a maintenance window (Multi-AZ reduces downtime to a failover):
aws rds modify-db-instance --db-instance-identifier orders-db --db-instance-class db.r7g.xlarge --apply-immediately
For Aurora, add a reader on the new class, fail over to it, then change the old writer: minimal disruption.
4. Aurora Standard vs I/O-Optimized
| Configuration | Pricing shape | Good when |
|---|---|---|
| Aurora Standard | Instance + storage + per I/O request | Low/moderate I/O |
| Aurora I/O-Optimized | Higher instance and storage price, no I/O charges | I/O is a large share of Aurora spend (AWS suggests evaluating above ~25%) |
Check last month's Aurora bill split (I/O usage type vs instance vs storage), then switch:
aws rds modify-db-cluster --db-cluster-identifier orders-aurora --storage-type aurora-iopt1 --apply-immediately
Switching has a frequency limit (check docs). Reserved instances apply to both.
5. Aurora Serverless v2 for variable load
Capacity scales in ACUs between a min and max you set; billed per ACU-second.
aws rds modify-db-cluster --db-cluster-identifier reporting-aurora \
--serverless-v2-scaling-configuration MinCapacity=0.5,MaxCapacity=16
aws rds modify-db-instance --db-instance-identifier reporting-aurora-1 --db-instance-class db.serverless
- Good for dev/test, spiky internal apps, and readers that follow reporting load.
- Steady high load is usually cheaper on provisioned instances (with reservations).
- Newer engine versions support a minimum of 0 ACU with auto-pause. Check your version, and accept the resume delay.
- Mixed clusters (provisioned writer, serverless readers) are allowed.
6. Storage
- RDS on gp3: provision IOPS/throughput separately; don't pay for io1/io2 unless you need its latency/durability.
- Storage autoscaling on, with a maximum. Storage can't shrink on RDS (migrate to a new instance to reduce).
- Aurora storage shrinks dynamically as data is deleted (on current versions).
7. Clean up
- Manual snapshots kept forever: list and expire per policy.
aws rds describe-db-snapshots --snapshot-type manual \
--query 'DBSnapshots[].[SnapshotCreateTime,DBSnapshotIdentifier,AllocatedStorage]' --output text | sort | head -30
- Read replicas nobody queries (
ReadIOPSnear zero, no connections). - Stopped instances: RDS restarts them after 7 days, so they cost more than you think if repeatedly "stopped".
- Extended Support charges for engines past end of standard support. Upgrade before they apply.
8. Commit after you've tuned
Buy reserved instances for the right-sized, steady fleet. Not before right-sizing.
Warning
Downsizing reduces memory, and with it the buffer cache. A database that fits in memory on r7g.2xlarge may start reading from disk on r7g.xlarge. Watch read latency and IOPS for a week after any downsizing.
Checklist
- Four weeks of CPU, memory, I/O and DB load reviewed
- Top SQL tuned first
- Current generation / Graviton classes
- Aurora I/O-Optimized decided from bill split
- Serverless v2 for variable workloads
- Snapshots, idle replicas and Extended Support cleaned up
- Reservations after right-sizing