CloudBurn Rules

RDS Rules

CloudBurn cost optimization rules for AWS RDS.

These rules cover the most common sources of unnecessary RDS spend: outdated instance classes, idle instances, missing reserved coverage, non-Graviton families, low utilization, extended support charges, orphaned snapshots, and excessive Performance Insights retention.

Rule IDScan TypeName
CLDBRN-AWS-RDS-1Discovery and IaCRDS Instance Class Not Preferred
CLDBRN-AWS-RDS-2DiscoveryRDS DB Instance Idle
CLDBRN-AWS-RDS-3DiscoveryRDS DB Instance Missing Reserved Coverage
CLDBRN-AWS-RDS-4Discovery and IaCRDS DB Instance Without Graviton
CLDBRN-AWS-RDS-5DiscoveryRDS DB Instance Low CPU Utilization
CLDBRN-AWS-RDS-6Discovery and IaCRDS DB Instance Unsupported Engine Version
CLDBRN-AWS-RDS-7DiscoveryRDS Snapshot Without Source DB Instance
CLDBRN-AWS-RDS-8IaCRDS Performance Insights Extended Retention

CLDBRN-AWS-RDS-1

RDS Instance Class Not Preferred

Scan type: Discovery and IaC

What it checks

Flags RDS DB instances using older-generation instance classes. The preferred classes offer better performance per dollar and are AWS's recommended choices for new and existing workloads.

Preferred instance classes:

ClassBest For
db.m8gGeneral-purpose workloads (Graviton4)
db.m8gdGeneral-purpose with local NVMe (Graviton4)
db.m8iGeneral-purpose workloads (Intel x86)
db.m7iGeneral-purpose workloads (Intel x86)
db.m7gGeneral-purpose workloads (Graviton3)
db.r8gMemory-intensive workloads (Graviton4)
db.r8gdMemory-intensive with local NVMe (Graviton4)
db.r8iMemory-intensive workloads (Intel x86)
db.r7iMemory-intensive workloads (Intel x86)
db.r7gMemory-intensive workloads (Graviton3)
db.t4gDev/test and burstable workloads (Graviton2)

Non-preferred classes that trigger findings: db.m1, db.m3db.m6*, db.r3db.r6*, db.t2db.t3. Instance families not in either list (e.g. db.m2) are treated as unclassified and do not trigger a finding.

Why it matters

Older-generation RDS instance classes are less performant and often more expensive than their current-generation equivalents. Graviton-based classes (db.m8g, db.r8g, db.t4g) provide 20-35% better price/performance than equivalent Intel x86 classes.

What triggers a finding

The DB instance class family matches one of the non-preferred generations listed above.

How to remediate

Modify the DB instance to use a preferred class. For production instances, use a Multi-AZ failover to minimize downtime:

aws rds modify-db-instance \
  --db-instance-identifier my-db \
  --db-instance-class db.m8g.large \
  --apply-immediately

For Graviton migrations, verify engine compatibility first — most engines on RDS support Graviton, but check the specific engine version and region availability.

IaC resources checked

IaC ToolResource Type
Terraformaws_db_instance
CloudFormationAWS::RDS::DBInstance

CLDBRN-AWS-RDS-2

RDS DB Instance Idle

Scan type: Discovery

What it checks

Flags RDS DB instances that have had zero database connections over the past 7 days. An instance with no connections is not serving any application traffic and is a candidate for deletion or suspension.

Why it matters

RDS instances are billed by the hour for provisioned compute and storage, regardless of whether any application is connected. An idle db.m7i.large Multi-AZ instance costs over $300/month. Development and staging databases that are forgotten after a project ends are a common source of unnecessary RDS spend.

What triggers a finding

maxDatabaseConnectionsLast7Days is 0.

How to remediate

  1. Verify no application is using the instance (check application logs, not just the CloudWatch metric)
  2. If truly idle, choose one of:
    • Stop the instance: RDS can be stopped for up to 7 days at a time. Storage charges continue but compute stops.
    • Delete the instance: Take a final snapshot, then delete.
    • Migrate to Aurora Serverless v2: For intermittent workloads, Aurora Serverless v2 scales to zero ACUs when idle, eliminating compute cost between bursts.
# Create final snapshot and delete
aws rds create-db-snapshot \
  --db-instance-identifier my-db \
  --db-snapshot-identifier my-db-final-snapshot

aws rds delete-db-instance \
  --db-instance-identifier my-db \
  --skip-final-snapshot

CLDBRN-AWS-RDS-3

RDS DB Instance Missing Reserved Coverage

Scan type: Discovery

What it checks

Flags RDS DB instances that have been running for at least 180 days without matching active reserved instance coverage. The rule matches instances to reservations by region, instance class, Multi-AZ configuration, and normalized engine (Aurora MySQL maps to mysql, Aurora PostgreSQL to postgres). Wildcard engine coverage is also consumed.

Why it matters

RDS Reserved Instances offer 30-60% savings over on-demand pricing depending on the term and payment option. An instance running on-demand for 6+ months is a strong candidate for a reservation. The longer you wait, the more savings you leave on the table.

What triggers a finding

All of the following must be true:

  • Instance status is available
  • Instance has been running for 180 or more days
  • No active reserved instance matches the instance's accountId:region:instanceClass:multiAz:normalizedEngine combination

How to remediate

  1. Review the flagged instance's class, engine, and Multi-AZ configuration
  2. Check AWS Cost Explorer RI recommendations for RDS
  3. Purchase a matching reserved instance:
aws rds purchase-reserved-db-instances-offering \
  --reserved-db-instances-offering-id <offering-id> \
  --db-instance-count 1

CLDBRN-AWS-RDS-4

RDS DB Instance Without Graviton

Scan type: Discovery and IaC

What it checks

Flags RDS DB instances using non-Graviton instance families when a Graviton-based equivalent exists. The review set includes db.m5, db.m5d, db.m6i, db.m6id, db.m6in, db.m7i, db.m8i, db.r5, db.r5b, db.r5d, db.r6i, db.r6id, db.r6in, db.r7i, db.r8i, db.t2, and db.t3.

Why it matters

Graviton-based RDS classes (db.m8g, db.r8g, db.t4g) offer 20-35% better price/performance than their Intel equivalents. For most database engines, the migration is straightforward and doesn't require application changes.

What triggers a finding

The instance class family is in the Graviton review set and is not already using a Graviton family.

How to remediate

Modify the instance to use a Graviton class. Test with a read replica first if you want to validate compatibility:

aws rds modify-db-instance \
  --db-instance-identifier my-db \
  --db-instance-class db.r8g.large \
  --apply-immediately

Check engine version compatibility, as some older engine versions don't support Graviton. Upgrade the engine version first if needed.

IaC resources checked

IaC ToolResource Type
Terraformaws_db_instance
CloudFormationAWS::RDS::DBInstance

CLDBRN-AWS-RDS-5

RDS DB Instance Low CPU Utilization

Scan type: Discovery

What it checks

Flags available RDS DB instances whose 30-day average CPU utilization stays at or below 10%. Consistently low CPU suggests the instance is over-provisioned for its workload.

Why it matters

An db.r7i.xlarge running at 5% average CPU is paying for 20x more compute than it needs. Right-sizing to a smaller instance class, or moving to Aurora Serverless v2 for variable workloads, can cut compute costs significantly with no impact on application performance.

What triggers a finding

Both conditions must be true:

  • Instance status is available
  • The 30-day average CPU utilization is at or below 10%

How to remediate

  1. Verify that the low CPU isn't masking a memory or I/O bound workload (check FreeableMemory and ReadIOPS/WriteIOPS metrics)
  2. If the instance is genuinely over-provisioned, downsize:
aws rds modify-db-instance \
  --db-instance-identifier my-db \
  --db-instance-class db.r8g.medium \
  --apply-immediately
  1. For workloads with highly variable traffic, consider Aurora Serverless v2 which scales compute automatically

CLDBRN-AWS-RDS-6

RDS DB Instance Unsupported Engine Version

Scan type: Discovery and IaC

What it checks

Flags RDS DB instances running MySQL 5.7 or PostgreSQL 11, which are past their standard support end-of-life and now incur extended support charges from AWS.

Why it matters

AWS charges extended support fees for engine versions past their standard EOL date. For MySQL 5.7, this adds $0.0725 per vCPU-hour in Year 1, doubling in Year 2. For PostgreSQL 11, the same pricing applies. On a Multi-AZ db.r7i.xlarge (4 vCPUs), that's an extra ~$210/month in Year 1 on top of the regular instance cost.

What triggers a finding

Either of these conditions:

  • Engine is mysql and version starts with 5.7
  • Engine is postgres and version starts with 11

How to remediate

Upgrade to a supported major version. For MySQL, the upgrade path is 5.7 -> 8.0. For PostgreSQL, 11 -> 14 or higher:

aws rds modify-db-instance \
  --db-instance-identifier my-db \
  --engine-version 8.0.39 \
  --allow-major-version-upgrade \
  --apply-immediately

Test the upgrade on a snapshot restore first. Review the MySQL 8.0 or PostgreSQL 14+ breaking changes that may affect your application queries.

IaC resources checked

IaC ToolResource Type
Terraformaws_db_instance
CloudFormationAWS::RDS::DBInstance

CLDBRN-AWS-RDS-7

RDS Snapshot Without Source DB Instance

Scan type: Discovery

What it checks

Flags RDS snapshots older than 30 days whose source DB instance no longer exists. Orphaned snapshots from deleted instances often accumulate indefinitely because nobody remembers to clean them up.

Why it matters

RDS snapshot storage costs $0.095 per GB-month. A 500 GB snapshot from a deleted production instance costs ~$47/month and will keep billing forever unless someone explicitly deletes it. Teams that frequently create and delete development databases are especially prone to snapshot accumulation.

What triggers a finding

Both conditions must be true:

  • The snapshot's source dbInstanceIdentifier does not match any currently active RDS instance in the same account and region
  • The snapshot's snapshotCreateTime is more than 30 days in the past

How to remediate

  1. Verify the snapshot isn't needed for compliance or disaster recovery
  2. If it's no longer needed, delete it:
aws rds delete-db-snapshot \
  --db-snapshot-identifier my-old-snapshot
  1. For long-term archival, export the snapshot to S3 first (cheaper storage) and then delete the RDS snapshot:
aws rds start-export-task \
  --export-task-identifier my-export \
  --source-arn arn:aws:rds:us-east-1:123456789012:snapshot:my-old-snapshot \
  --s3-bucket-name my-archive-bucket \
  --iam-role-arn arn:aws:iam::123456789012:role/rds-export-role \
  --kms-key-id alias/my-key

CLDBRN-AWS-RDS-8

RDS Performance Insights Extended Retention

Scan type: IaC

What it checks

Flags DB instances that enable Performance Insights with retention beyond the included 7-day free period. Extended retention (up to 24 months) incurs additional charges.

Why it matters

Performance Insights includes 7 days of data retention at no extra cost. Extending retention to 731 days (24 months) costs $2.48/vCPU-month for instances outside the free tier. A db.r6g.4xlarge with 16 vCPUs would pay ~$39.68/month for extended retention. Unless you need long-term performance trend analysis, the free 7-day window is sufficient for most troubleshooting.

What triggers a finding

performanceInsightsEnabled is true AND performanceInsightsRetentionPeriod is a number greater than 7.

How to remediate

Set performance_insights_retention_period to 7 in Terraform, or set PerformanceInsightsRetentionPeriod to 7 in CloudFormation. Only extend retention if you have a specific need for long-term performance data.

IaC resources checked

IaC ToolResource Type
Terraformaws_db_instance
CloudFormationAWS::RDS::DBInstance

See Also