RDS instances tend to get sized once, at launch, for a workload nobody has fully measured yet — and then never revisited, because a running database is the last thing anyone wants to touch without a clear reason. That combination is exactly why oversized RDS instances are one of the most common findings in any account that's been running for more than a year.
Reading utilization correctly
Low average CPU on its own isn't proof of an oversized instance — batch jobs, reporting queries, and month-end processing can make a snapshot in time misleading either direction. What's worth trusting is a full two-to-four-week window across CPU utilization, freeable memory, and connection count together. An instance class that's consistently well below its ceiling on all three, including through whatever periodic workload the database handles, is a genuinely strong signal — not a guess.
A real four-week CloudWatch window, not a snapshot — this is the shape to look for, not a specific instance's numbers.
Multi-AZ doubles the instance cost, on purpose
A Multi-AZ deployment runs a synchronously replicated standby in a second Availability Zone, and that standby is billed as a full second instance. That's the correct trade for production databases where the failover matters. It's a much harder trade to justify for staging, QA, or dev databases running the same instance class and same Multi-AZ configuration as production, purely because that's how the environment was templated.
Storage autoscaling needs a ceiling
Storage autoscaling is useful for avoiding an out-of-space incident, but enabled without a maximum threshold, it will keep growing to absorb whatever is filling the disk — including bloat, unvacuumed tables, or forgotten large objects that nobody would choose to pay to store indefinitely if they saw it happening. A ceiling doesn't prevent legitimate growth; it forces a conscious decision before storage cost runs away unnoticed.
When the workload doesn't fit the provisioned-instance model
Not every database has steady, predictable load. For workloads that are genuinely spiky — bursty traffic, dev/test databases only active part of the day, unpredictable batch workloads — a provisioned instance sized for peak sits idle most of the time. Aurora Serverless v2 scales capacity to actual load and is worth evaluating specifically for that shape of workload, rather than as a default.
Where to look
- RDS Performance Insights for query-level load, not just instance-level averages.
- CloudWatch CPUUtilization, FreeableMemory, and DatabaseConnections over a full multi-week window, not a point-in-time check.
- Non-production Multi-AZ databases running the same class as production — a very common, very easy-to-verify finding.
- Storage autoscaling configuration on every instance — confirm a ceiling exists and that it reflects an intentional decision, not the default.