Migrating EC2 to Graviton — Real Savings and a Migration Checklist

5 min read

The single biggest structural lever on instance pricing is migrating to Graviton. Graviton is AWS’s own arm64 CPU line; comparable instances price around 20% below their x86 counterparts, and with performance factored in, the price-performance gap widens to as much as 40% depending on the generation. Where the previous posts cut waste, this one does the same work at a cheaper rate. It’s organized as a checklist answering “can our workload move?”

The generation landscape — what to pick #

As of mid-2026:

GenerationRepresentative instancesStatus
Graviton2m6g, c6g, r6g, t4gMature, cheap baseline
Graviton3m7g, c7g, r7gMature, widely used
Graviton4m8g, c8g, r8g, x8gCurrent mainstream
Graviton5m9g, m9gdGA June 2026 (general purpose first); c9g and r9g expected later this year

Graviton5 is quoted at up to 25% more compute than Graviton4, but for someone starting a migration, generation choice is secondary. The work of moving to arm64 is generation-independent — once you’re on arm64, later generation swaps are just instance type changes. For a new migration, default to the Graviton4 families based on regional availability and price. For burstable dev environments, t4g remains excellent value.

Migration difficulty — how hard it is, workload by workload #

The whole question reduces to one thing: can you produce binaries that run on arm64?

  • Easy — interpreted and VM languages: Python, Node.js, Ruby, PHP, and JVM (Java, Kotlin) applications need almost no code changes once the runtime is arm64. Package repositories of current distributions support arm64 as a matter of course.
  • Moderate — native dependencies: Python C extensions and Node native modules mostly ship arm64 wheels and prebuilds, but an old pinned version can trip you up. Verify in CI that a rebuild resolves it.
  • Verify directly — your own native code, third-party agents: Go and Rust cross-compile easily; for C/C++ using SIMD intrinsics (the AVX families), check the port to NEON/SVE. For third-party binaries like APM and security agents, confirm the vendor’s arm64 support. Major vendors mostly do; the in-house agent that’s been rolling along for years is the usual surprise.
  • No-go — x86-only commercial software: if the vendor ships no arm64 build, exclude that workload. You don’t have to move everything.

The migration checklist #

The actual sequence:

  1. Dependency inventory — list the application’s native dependencies, base images, and installed agents, and confirm arm64 support. If nothing blocks here, half the work is done.
  2. Multi-architecture image builds — for container workloads, bundle amd64 and arm64 under one tag with docker buildx; the build process is covered in Docker Advanced #2. Emulated builds on x86 CI runners are slow — arm64-native runners (GitHub Actions arm64 runners, CodeBuild’s Graviton environments) are faster.
  3. Functional and performance validation in staging — beyond functional tests, load-test and compare p99 latency and throughput against the x86 baseline. Usually equal or better, but it varies by workload, so measurement is the standard.
  4. Canary cutover — mix a few arm64 nodes into the ASG or EKS node group and let them take a slice of real traffic. With multi-architecture images, the orchestrator pulls the right architecture per node on its own. If it holds, raise the ratio to full cutover.
  5. Extend to managed services — many are easier than EC2. RDS, Aurora, and ElastiCache switch by changing the instance class to a g family (no application changes); Lambda drops execution cost by 20% when the function architecture is set to arm64. Fargate supports arm64 too. Starting here to build confidence is a perfectly sensible sequence.

Traps that catch people #

  • Old AMIs and custom AMI pipelines — Packer templates that baked x86 AMIs need arm64 duplicates. Source AMI IDs differ per architecture.
  • x86-only wheels pinned in lock files — if the lock file pins x86 wheel hashes, arm64 installs fall back to source builds and get slow or fail. Regenerate as a multi-platform lock.
  • Assuming “performance will be the same” — most web and API workloads are equal or better, but a library optimized for x86 SIMD can invert that. Don’t skip the measurement in step 3.
  • Image tag mistakes during mixed operation — during the transition, deploying an amd64-only image to an arm64 node is an easy accident. Enforcing multi-architecture manifests by default prevents it.

Computing the savings #

The effect is “rate difference × share migrated.” On-demand rates run around 20% below same-generation x86, and Savings Plans multiply on top. One caution: EC2 Instance Savings Plans are tied to an instance family. If a migration is planned, a commitment that follows you across architectures — Compute Savings Plans — is safer than one that pins the family. Commitment structures are covered in SAA Domain 4-1.

Summary #

  • Graviton migration structurally cuts around 20% off comparable x86 rates, and once on arm64, generation swaps (Graviton4 mainstream today, Graviton5 GA) are just type changes.
  • The crux is producing arm64 binaries. Interpreted and JVM stacks are easy; native dependencies and third-party agents are the checkpoints.
  • The order: dependency inventory → multi-arch images → staging measurements → canary → expand. Don’t skip the measurements.
  • RDS, ElastiCache, and Lambda are easier than EC2 — a good place to start building confidence.
  • With a migration planned, choose Compute Savings Plans over family-pinned commitments.
X