RDS on Graviton: the basics
What does it mean to migrate an RDS instance to ARM?
AWS Graviton is a family of ARM-based processors AWS designs in-house. The RDS Graviton instance classes (db.t4g, db.m6g/m7g, db.r6g/r7g) run the same managed database engines you already use (PostgreSQL, MySQL, MariaDB, Aurora), but on ARM silicon instead of Intel or AMD. The engine binary is ported and maintained by AWS; your application sees an identical Postgres or MySQL protocol on the wire.
The Cost Optimization Hub flags an RDS instance as a Graviton migration candidate when it's running on an x86 instance class (db.m5, db.r5, db.t3) and the engine version supports an ARM-equivalent. AWS publishes a roughly 20% better price-performance gain at the list rate: same vCPU and memory specs, lower hourly cost, modestly better throughput on most workloads.
What gets flagged isn't always actionable. Graviton requires a minimum engine version per family: PostgreSQL 12.3+, MySQL 8.0.17+, Aurora MySQL 2.10.x or 3.x, Aurora PostgreSQL 12.6+. Reserved DB Instances are tied to instance family, so an active RI on a db.m5 will be stranded if you move to db.m6g. And custom extensions, partner-built monitoring agents, or rare query plans can behave differently. The check is high-signal, not zero-risk.
In this lesson you'll learn how Graviton delivers price-performance gains for managed databases, the engine version and instance class mapping you need to check before applying, and how to safely move a live RDS instance to ARM with no application changes. You'll see real ModifyDBInstance calls, the Multi-AZ failover sequence, and what to monitor in the seven days following the swap.
The Reserved Instance trap
Teams often look at a Graviton recommendation, see a 20% list-price saving, and apply it the same afternoon, without checking their Reserved DB Instance coverage. RIs are size, region, AND engine-family specific: a one-year db.r5.2xlarge RI in eu-west-1 does not apply to db.r6g.2xlarge in the same region. The day the instance class changes, the RI becomes idle and the new Graviton instance bills at the full on-demand rate. The net result for the first 8-10 months is usually more expensive than where you started. An RDS Reserved DB Instance can't be cancelled, sold, or transferred, so the fix is to time the migration to the RI's expiry, not to apply the change and deal with the stranded reservation after.
Graviton migration in action
Marco runs platform engineering at a B2B SaaS. The Cost Optimization Hub flags their production analytics database (a db.r5.4xlarge Multi-AZ Postgres) as a Graviton candidate, with an estimated $1,840/month saving against the list rate.
He checks the engine version first: PostgreSQL 15.4. Well above the 12.3 minimum. Then he checks Reserved Instance coverage: the database is on-demand, no RI to release. Read replica? One, in the same region, used by the BI tool. He'll upgrade the replica first, validate, then upgrade the primary in next Sunday's maintenance window.
He runs the ModifyDBInstance call with ApplyImmediately set to false, so the change queues for the maintenance window rather than triggering an immediate failover at 2pm on a Wednesday.
First, confirm the instance class and engine version meet Graviton's requirements.
Pre-flight check: instance class, engine version, Multi-AZ posture.
Apply the change deferred to the maintenance window: no immediate failover, no surprise at 2pm.
ModifyDBInstance queues the class change for the maintenance window.
Graviton migration under the hooddeep dive
When you change the DBInstanceClass on a Multi-AZ Postgres instance, RDS does the actual swap in a specific order to minimise downtime. It provisions the new Graviton standby first in the secondary AZ, replicates the WAL to it, then triggers a failover from the existing x86 primary to the new ARM standby. The failover itself takes 60-120 seconds in most accounts, the same window you'd see for an OS patch or any other DB-level maintenance. Once traffic is on the new Graviton instance, RDS upgrades what used to be the primary to ARM as well and re-establishes synchronous replication.
Single-AZ instances don't get this dance. The instance is stopped, the volume is detached, a new Graviton instance is attached to the same EBS volume, and the database starts up on ARM. Downtime is typically 5-15 minutes, dominated by the InnoDB or Postgres recovery on startup. For anything customer-facing, Multi-AZ is non-negotiable for a Graviton swap: single-AZ means a real outage.
The Postgres or MySQL binary running on Graviton is compiled by AWS for ARM and bundled into the engine version. There's no version mismatch concern between the old and new instance; they're running the same Postgres 15.4 binary, just on different CPU architectures. The catalog, the indexes, the WAL, and the on-disk format are byte-compatible across ARM and x86, which is why the same EBS volume can be re-attached. The only thing that changes is the CPU underneath the engine.
# Read replicas first — validates the engine on ARM with no impact to primary.
aws rds modify-db-instance \
--db-instance-identifier analytics-prod-replica \
--db-instance-class db.r6g.4xlarge \
--apply-immediately
# Wait for the replica to come back as 'available'.
aws rds wait db-instance-available \
--db-instance-identifier analytics-prod-replica
# Then queue the primary for the maintenance window.
aws rds modify-db-instance \
--db-instance-identifier analytics-prod \
--db-instance-class db.r6g.4xlarge \
--no-apply-immediately What is the impact of staying on x86 RDS?
The headline cost is the price-performance gap. AWS publishes Graviton instance classes at roughly 10% cheaper hourly than the equivalent x86 generation, and benchmarks 20% better throughput on average, so the effective price-performance gain compounds to around 20%. On a db.r5.4xlarge Multi-AZ Postgres at on-demand rates in us-east-1, that's about $1,800/month of pure margin, recurring forever. Multiply across the fleet of databases in any non-trivial estate and it's six figures a year of free money sitting on the table.
The second-order cost is the generation tax. Newer Graviton instances (m7g, r7g) are released regularly with another ~10-15% gain on top of the previous generation, and AWS has signalled clearly that the bulk of future investment is on ARM. Staying on x86 means falling further behind the price-performance curve every product cycle, and it gets harder to migrate the longer the database has been running.
There's a Reserved Instance trap working against you here too: an x86 RI keeps billing whether or not the database it covered still exists, and an RDS Reserved DB Instance can't be cancelled, sold, or transferred. So if you migrate before the term ends you pay the stranded RI alongside the new on-demand Graviton instance, and the saving only turns net-positive once the Graviton gains exceed that double cost. The clean move is to time the migration to the RI's expiry; migrating early only pays off when there are just a few months left on the term.
Finally, there's an operational risk in the other direction: Graviton migrations are routine but not trivial. Doing one in a panic in the middle of a budget cut, without a maintenance window or a replica to validate against, is how you end up with a corrupted index or a failed extension at 3am. The lesson is to do it deliberately and on a cadence, not when the CFO finally notices the database line on the bill.
How do you migrate to Graviton safely?
Graviton migration is a four-step loop: confirm eligibility, validate on a replica, swap the primary in a window, then watch the metrics. Each step takes minutes; skipping any of them is how the migration ends up in a post-mortem.
1. Confirm engine and RI eligibility first
Check the engine version against the minimums (PostgreSQL 12.3+, MySQL 8.0.17+, MariaDB 10.4.13+, Aurora MySQL 2.10.x or 3.x, Aurora PostgreSQL 12.6+). If you're below the minimum, upgrade the engine first as a separate change. Then check Reserved DB Instance coverage in describe-reserved-db-instances: if you have an active x86 RI for this instance, decide whether to wait for its term to end before migrating or to migrate now and pay it out, because an RDS RI can't be cancelled, sold, or transferred. Don't skip this; an ignored RI is the single most common reason a Graviton migration ends up increasing the bill.
2. Migrate a read replica first
If a read replica exists, upgrade it to Graviton with ApplyImmediately=true a few days before touching the primary. The replica streams from the x86 primary using logical or physical replication that doesn't care about the underlying CPU, so it's a perfect canary. Run your BI queries, run any custom extensions, watch p99 latency on the replica for 48-72 hours. Any plan regressions or extension incompatibilities will show up here, not on the primary at 3am.
3. Queue the primary change for a maintenance window
Use ModifyDBInstance with --no-apply-immediately so the change waits for the next PreferredMaintenanceWindow. For Multi-AZ, AWS provisions the new Graviton standby, fails over (60-120s), then upgrades the old primary in the background. For Aurora clusters, modify the cluster and each instance, and remember Global Database setups require both regions. Single-AZ takes a real outage; if the database backs customer traffic, run a one-off Multi-AZ conversion first.
4. Monitor for seven days post-cutover
Watch CloudWatch p99 latency, CPU utilisation, and connection counts for at least a week. Pull AWS Performance Insights for any new top-SQL entries; most Graviton migrations are uneventful, but a small fraction surface query plan changes or extension behaviour differences. Have a rollback path ready: a recent snapshot, or an x86 read replica you can promote if something genuinely regresses. The rollback rarely fires, but having the option keeps the migration boring.
# Pre-flight: list all RDS instances on x86 classes with engine versions Graviton supports.
aws rds describe-db-instances \
--query 'DBInstances[?starts_with(DBInstanceClass, `db.m5`) || starts_with(DBInstanceClass, `db.r5`) || starts_with(DBInstanceClass, `db.t3`)].[DBInstanceIdentifier,DBInstanceClass,Engine,EngineVersion]' \
--output table
# Post-flight: check Performance Insights for top-SQL changes after migration.
aws pi describe-dimension-keys \
--service-type RDS \
--identifier db-XXXXXXXX \
--metric db.load.avg \
--group-by '{"Group":"db.sql_tokenized","Limit":10}' \
--start-time $(date -u -d '7 days ago' +%s) \
--end-time $(date -u +%s) Quick quiz
Question 1 of 5You have a Multi-AZ db.r5.2xlarge running PostgreSQL 15.4 with one read replica and an active 1-year Reserved DB Instance. Cost Optimization Hub recommends db.r6g.2xlarge. What's the right order of operations?
You scored
0 / 5
Keep learning
Dig deeper into RDS on Graviton and the operational details around the migration.
- AWS RDS on Graviton documentation Official instance class reference, including Graviton family mappings and engine version requirements.
- ModifyDBInstance API reference Full parameter set for instance modifications, including ApplyImmediately semantics and pending modified values.
- Reserved DB Instances How RI coverage is scoped by instance family and engine: essential reading before any class change.
- AWS Performance Insights Top-SQL and load metrics for spotting post-migration plan regressions during the seven-day monitoring window.
You've completed Migrate RDS to Graviton (ARM). You now know how to confirm engine and RI eligibility, validate on a read replica, queue the primary change for a maintenance window, and monitor for seven days afterwards. The next time the Cost Optimization Hub flags a Graviton candidate, you'll have the four-step loop ready, and you won't get caught by the Reserved Instance trap.
Back to the library