AWS spent the first half of 2026 rolling out its fifth generation of custom Arm silicon, and the rollout has been unusually fast even by AWS standards. Amazon EC2 M9g and M9gd instances powered by AWS Graviton5 processors went generally available on June 10, 2026. C9g and C9gd followed on June 30, 2026, and R9g and R9gd landed on July 1, 2026. Three new instance families in three weeks is a lot to absorb if your fleet is still running on x86 or even on Graviton4. This tutorial walks through the actual migration process: assessing what you have, testing compatibility, rebuilding your container and CI pipelines for arm64, moving your Terraform, and cutting production traffic over without an outage.
Graviton5 is not a minor refresh. AWS’s own processor page describes a 192-core design built on a four-chiplet layout, with roughly 5x the cache of the prior generation and up to 33% lower inter-core latency, aimed squarely at throughput-heavy workloads like agentic AI pipelines, real-time analytics, and high-concurrency web services. The R9g and R9gd memory-optimized instances deliver up to 25% better compute performance than R8g, the Graviton4-based memory family they replace, according to AWS’s July 2026 launch post. If you migrated to Graviton4 in 2024 or 2025 and assumed that was the end of the story, it wasn’t, and the search volume backs that up: “aws graviton” pulls roughly 1,300 monthly US searches with low keyword competition per DataForSEO data pulled in August 2026, a sign that a lot of infrastructure teams are working through exactly this decision right now.
Don't miss new tech stories on Google
Add Tech Insider once in the Google app and our stories appear in your news suggestions.
What Changed With AWS Graviton5
Before touching a single instance, it helps to know exactly what you’re migrating to and why AWS built it this way. Graviton5 is not just a clock-speed bump. AWS redesigned the core layout, expanded the cache hierarchy, and added architectural changes specifically to keep continuous, high-throughput CPU-bound work (the kind agentic AI orchestration and code generation pipelines generate) from stalling on memory latency. The chip runs at 3.3 GHz across a four-chiplet, 192-core design, which is a meaningfully different shape than the monolithic dies used in earlier Graviton generations.
Three EC2 families ship on Graviton5 today, and each maps to a specific workload type rather than being a blanket replacement for its Graviton4 counterpart:
- M9g / M9gd: general-purpose, the direct successor to M8g. M9gd adds local NVMe instance storage (for example, m9gd.8xlarge ships with a single 1,900 GB NVMe SSD). Generally available since June 10, 2026 in us-east-1, us-east-2, us-west-2, and eu-central-1.
- C9g / C9gd: compute-optimized, successor to C8g. Offered in 11 sizes from medium through 48xlarge, including bare-metal options, and the first Graviton family to support PCIe Gen 6 on the latest Nitro platform. GA since June 30, 2026, same four regions with more planned.
- R9g / R9gd: memory-optimized, successor to R8g, aimed at databases, in-memory caches, and real-time analytics. AWS claims up to 25% better compute performance than R8g. GA since July 1, 2026.
Graviton4 hasn’t gone anywhere. C8g, M8g, R8g, R8gn, X8g, and I8g remain fully supported and are still the broader, more mature option, especially for storage-optimized and high-memory shapes that Graviton5 hasn’t reached yet. If your workload needs X8g-class memory ratios or I8g-class local storage, you’re staying on Graviton4 for now, and that’s a perfectly reasonable place to land after this tutorial.
| Generation | Core count | Instance families | GA date | Best fit |
|---|---|---|---|---|
| Graviton3 | 64 cores/chip | C7g, M7g, R7g | 2021 | Legacy fleets, lowest migration urgency |
| Graviton4 | 96 cores/chip | C8g, M8g, R8g, R8gn, X8g, I8g | 2024 (R8gn: Sept. 15, 2025) | High-memory, storage-optimized, broadest region coverage |
| Graviton5 | 192 cores, 4-chiplet design | M9g/M9gd, C9g/C9gd, R9g/R9gd | June 10 – July 1, 2026 | Agentic AI, high-throughput compute, latency-sensitive services |
Prerequisites: What You Need Before You Start
Get these in place before Step 1. Migrating to a new CPU architecture touches more of your stack than a typical instance resize, and skipping prep work here is the most common reason teams stall out halfway through.
- AWS CLI v2 (2.27.x or later), configured with an IAM identity that has permissions for EC2, Auto Scaling, Compute Optimizer, and Cost Explorer
- Terraform 1.15.x or later, if your infrastructure is managed as code (this tutorial’s examples assume Terraform, but the same instance-family swap applies to CloudFormation or CDK)
- Docker Engine 27.x or later with Buildx enabled, for building multi-architecture container images
- An existing EC2 fleet or Auto Scaling Group running on x86 (Intel/AMD) or Graviton4 instances, so you have a baseline to compare against
- AWS Compute Optimizer enabled on the account (free, but must be opted in under account settings before it generates recommendations)
- A CI/CD pipeline (GitHub Actions, GitLab CI, or CodeBuild) you can modify to add an arm64 build target
- For containerized workloads: base images with published arm64 variants (Amazon Linux 2023, Ubuntu 24.04 LTS, Debian 13, and the official Node.js, Python, and Java Docker Hub images all ship arm64 builds)
- Root access or sudo on a Linux box (or a Cloud9/CloudShell session) to run benchmark and compatibility checks
Confirm your CLI versions before moving on:
aws --version
# aws-cli/2.27.4 Python/3.12.6 Linux/6.8.0 exe/x86_64.ubuntu.24
terraform version
# Terraform v1.15.9
# on linux_amd64
docker buildx version
# github.com/docker/buildx v0.19.2
Step 1: Inventory Your Current Fleet
Start by getting an honest picture of what’s actually running. Don’t rely on memory or an outdated architecture diagram. Pull the live instance list and group it by family and CPU architecture.
aws ec2 describe-instances \
--query "Reservations[].Instances[].[InstanceId,InstanceType,State.Name,Tags[?Key=='Name'].Value|[0]]" \
--output table
# ---------------------------------------------------------------
# | DescribeInstances |
# +----------------------+---------------+---------+-------------+
# | i-0a1b2c3d4e5f6g7h8 | m6i.xlarge | running | api-prod-1 |
# | i-0b2c3d4e5f6g7h8i9 | c6i.2xlarge | running | worker-2 |
# | i-0c3d4e5f6g7h8i9j0 | m8g.large | running | api-prod-2 |
# ---------------------------------------------------------------
Any instance type ending in a plain letter with no “g” (m6i, c7i, r7a) is x86. Anything ending in “g” (m8g, c7g, r8g) is already on Graviton, just a prior generation. This distinction matters because the migration path is different: x86 to Graviton5 requires an architecture change and a full compatibility pass, while Graviton4 to Graviton5 is usually a same-architecture instance-type swap with far less risk.
Step 2: Pull Compute Optimizer’s Graviton Recommendations
AWS Compute Optimizer analyzes your CloudWatch utilization history and flags instances that are good Graviton candidates based on actual CPU, memory, and network usage rather than guesswork. It won’t recommend Graviton5 specifically for instances with sustained sub-70% utilization patterns that don’t need the extra headroom, which is a useful sanity check before you spend engineering time on a migration that won’t move the needle.
aws compute-optimizer get-ec2-instance-recommendations \
--instance-arns arn:aws:ec2:us-east-1:123456789012:instance/i-0a1b2c3d4e5f6g7h8 \
--query "instanceRecommendations[].recommendationOptions[?platformDifferences[0]=='Architecture']"
Instances flagged with an Architecture platform difference are the ones Compute Optimizer thinks are worth moving to Graviton. Cross-reference that list against Step 1’s inventory and you have your migration candidate pool, prioritized by expected impact rather than by whichever service happens to be top of mind.
Step 3: Audit Software Dependencies for arm64 Support
This is the step people skip and regret. Before launching a single Graviton5 instance, check every binary dependency, compiled extension, and container base image your application relies on for arm64 (aarch64) builds. Most modern language runtimes and popular libraries publish arm64 binaries by default now, but legacy internal tooling, older commercial agents (APM agents, some licensed database drivers), and anything compiled from source with architecture-specific flags are the usual holdouts.
- Language runtimes: Node.js 20+, Python 3.11+, Java (Amazon Corretto 17/21), and Go 1.21+ all ship native arm64 builds with no code changes required for pure-language logic
- Native extensions: Python packages with C extensions (numpy, psycopg2, cryptography) need arm64 wheels. Check PyPI before assuming pip will just work
- Commercial agents and licensed software: APM agents, security scanners, and licensed database clients sometimes lag on arm64 support by a release cycle or two. Check vendor documentation directly
- Container base images: confirm your exact tag (not just the image name) has an arm64 manifest. Some older or pinned tags were built x86-only and never got a multi-arch update
Step 4: Launch a Graviton5 Test Instance
Spin up a single M9g instance in an isolated test VPC before touching anything production-facing. This gives you a real environment to validate compatibility findings from Step 3 against actual hardware.
aws ec2 run-instances \
--image-id ami-0abcdef1234567890 \
--instance-type m9g.xlarge \
--key-name my-test-key \
--subnet-id subnet-0a1b2c3d4e5f6g7h8 \
--security-group-ids sg-0a1b2c3d4e5f6g7h8 \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=graviton5-compat-test}]'
# {
# "Instances": [{
# "InstanceId": "i-0d4e5f6g7h8i9j0k1",
# "InstanceType": "m9g.xlarge",
# "Architecture": "arm64",
# "State": {"Name": "pending"}
# }]
# }
Use an arm64-native AMI: Amazon Linux 2023, Ubuntu 24.04 LTS arm64, or Debian 13 arm64 all work. Trying to boot an x86 AMI on an m9g instance will simply fail at launch, which is a fast and harmless way to confirm you have the right image ID before you go further.
Step 5: Build Multi-Architecture Container Images
If your workload runs in containers (on ECS, EKS, or plain EC2 with Docker), you need images that support both x86_64 and arm64 during the migration window, so you can run old and new instances side by side without maintaining two separate image pipelines. Docker Buildx handles this natively.
docker buildx create --name graviton-builder --use
docker buildx build \
--platform linux/amd64,linux/arm64 \
--tag your-account.dkr.ecr.us-east-1.amazonaws.com/api-service:v2.4.0 \
--push .
# [+] Building 84.2s (22/22) FINISHED
# => [linux/amd64 8/8] exporting to image
# => [linux/arm64 8/8] exporting to image
# => pushing manifest list
Buildx produces a single manifest list tagged with one name that resolves to the correct architecture automatically at pull time. ECS and EKS both handle this transparently: a task definition or pod spec pointing at that tag pulls the arm64 layer when scheduled onto a Graviton5 node and the amd64 layer when scheduled onto an x86 node, with zero changes to your deployment manifests.
Step 6: Update CI/CD for arm64 Builds
Your build pipeline needs to actually produce the arm64 layer on every commit, not just when someone remembers to run Buildx manually. Here’s a GitHub Actions job that builds and pushes both architectures on every push to main:
name: Build multi-arch image
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/github-actions-ecr
aws-region: us-east-1
- uses: docker/login-action@v3
with:
registry: 123456789012.dkr.ecr.us-east-1.amazonaws.com
- uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: 123456789012.dkr.ecr.us-east-1.amazonaws.com/api-service:${{ github.sha }}
The setup-qemu-action step matters. Without it, GitHub’s amd64 runners can’t emulate the arm64 build target and the job fails with an obscure exec-format error. This is one of the most common CI failures teams hit in the first week of a Graviton migration, and it’s a one-line fix once you know to look for it.
Step 7: Migrate Your Terraform
Update your instance-type references and, if you’re using Auto Scaling Groups, switch to a mixed instances policy so you can roll Graviton5 capacity in gradually instead of replacing the whole fleet in one deployment. See Terraform’s own documentation for the full mixed_instances_policy schema.
resource "aws_autoscaling_group" "api" {
name = "api-prod-asg"
desired_capacity = 6
min_size = 4
max_size = 12
vpc_zone_identifier = var.private_subnet_ids
mixed_instances_policy {
instances_distribution {
on_demand_base_capacity = 2
on_demand_percentage_above_base_capacity = 50
spot_allocation_strategy = "capacity-optimized"
}
launch_template {
launch_template_specification {
launch_template_id = aws_launch_template.api.id
version = "$Latest"
}
override {
instance_type = "m9g.xlarge"
}
override {
instance_type = "m9gd.xlarge"
}
override {
instance_type = "m8g.xlarge"
}
}
}
}
Listing m8g.xlarge as a fallback override keeps the ASG able to launch Graviton4 capacity if Graviton5 hits a capacity constraint in your region during the rollout. Remember, as of this writing Graviton5 is only available in four regions, so a multi-region deployment needs this kind of fallback built in from day one.
Step 8: Benchmark Before You Commit
Run the same load test against your x86 or Graviton4 baseline and your new Graviton5 test instance before touching production traffic. A synthetic CPU benchmark with sysbench gives you a quick, repeatable number to compare:
sysbench cpu --cpu-max-prime=20000 --threads=4 run
# Graviton5 (m9g.xlarge) output:
# CPU speed:
# events per second: 4821.33
# General statistics:
# total time: 10.0002s
# total number of events: 48219
# Graviton4 (m8g.xlarge) baseline output:
# CPU speed:
# events per second: 3688.71
# General statistics:
# total time: 10.0001s
# total number of events: 36891
A synthetic benchmark is a sanity check, not a migration decision by itself. Run your actual application’s load test or replay real production traffic through a shadow deployment for a result you can actually trust. AWS’s own R9g launch materials report up to 25% better compute performance than R8g for memory-optimized workloads specifically, which is the kind of number you want to reproduce against your own traffic pattern before rolling out broadly.
Step 9: Migrate Stateful Services Separately
Databases and caches need a different playbook than stateless compute. Migrating a managed database is a modify-instance-class operation rather than a rebuild, but it still causes a brief failover, and the specifics differ depending on which managed service you’re running.
Aurora and RDS
Amazon Aurora and Amazon RDS both support Graviton-based instance classes, but confirm your specific engine version has a published Graviton5-class instance type available in your region before you commit, since coverage rolls out family-by-family and engine-by-engine, not all at once. Schedule the instance-class modification in a maintenance window, and if you’re running a Multi-AZ deployment, modify the standby replica first, let it fail over, then modify what was previously the primary, so you never take an outage window larger than a single failover.
One real data point worth sizing your expectations against: per AWS’s June 2026 M9g launch announcement, HubSpot’s engineering team migrated MySQL workloads onto M9g instances and reported up to 60% faster query execution times after the move. That’s an unusually large gain and it’s workload-specific: a MySQL fleet under similar contention patterns is a good candidate to see something in that range, but treat it as a ceiling to test against, not a guaranteed outcome for every workload.
ElastiCache and Self-Managed Caches
Amazon ElastiCache for Redis and Memcached support Graviton instance classes through the same modify-replication-group workflow used for any instance-type change, and because caches are typically clustered with automatic failover already configured, the operation is usually less risky than a primary database move. If you’re running Redis or Memcached yourself on raw EC2 instead of through ElastiCache, treat it like any other stateful service: stand up a Graviton5 replica, let it sync, promote it, then decommission the old node once you’ve confirmed cache hit rates and latency look normal.
Step 10: Roll Out Gradually With Weighted Traffic
Don’t cut the whole fleet over at once. If you’re behind an Application Load Balancer, use weighted target groups to shift traffic in stages (10%, then 25%, then 50%, then 100%) with a soak period between each step to watch error rates and latency.
aws elbv2 modify-listener \
--listener-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:listener/app/api-prod/abc123/def456 \
--default-actions '[{
"Type": "forward",
"ForwardConfig": {
"TargetGroups": [
{"TargetGroupArn": "arn:aws:...:targetgroup/api-x86/111", "Weight": 90},
{"TargetGroupArn": "arn:aws:...:targetgroup/api-graviton5/222", "Weight": 10}
]
}
}]'
Increase the Graviton5 weight only after each stage clears a full business-hours cycle without an increase in 5xx errors or p99 latency. This is slower than a big-bang cutover, but it’s the difference between catching a subtle architecture-specific bug at 10% of traffic versus discovering it at 100%.
Step 11: Validate Networking and Instance Metadata
Graviton5 instances use the same Elastic Network Adapter (ENA) and Nitro-based networking stack as prior generations, so most networking configuration carries over unchanged. Still, verify these three things explicitly rather than assuming:
- Security groups reference security group IDs, not instance-type-specific rules, so there’s nothing to change here, but confirm no legacy rule hardcoded an old instance type
- Enhanced networking (ENA) is enabled by default on Graviton5. If you’re using a custom AMI built from an older snapshot, confirm the ENA driver module is present
- Instance Metadata Service v2 (IMDSv2): if your account enforces IMDSv2-only, confirm your application’s SDK and any sidecar agents have been updated to use token-based metadata requests, since this trips up teams more often on new instance launches than the architecture change itself
Step 12: Monitor Cost and Decommission Old Capacity
Once Graviton5 is carrying 100% of production traffic and has soaked for at least one full billing cycle, pull a Cost Explorer comparison to confirm the savings materialized in your actual bill, not just in the on-demand rate card.
aws ce get-cost-and-usage \
--time-period Start=2026-07-01,End=2026-08-01 \
--granularity MONTHLY \
--metrics "UnblendedCost" \
--group-by Type=DIMENSION,Key=INSTANCE_TYPE \
--filter '{"Dimensions":{"Key":"SERVICE","Values":["Amazon Elastic Compute Cloud - Compute"]}}'
Compare the INSTANCE_TYPE breakdown month over month. Once you’re satisfied the new fleet is stable and the numbers check out, terminate the old x86 or Graviton4 instances, deregister their AMIs if they’re no longer needed, and remove the old target groups and launch templates from Terraform so the next engineer doesn’t accidentally scale the retired fleet back up.
Migrating EKS and Kubernetes Node Groups to Graviton5
If your workloads run on Amazon EKS instead of raw EC2, the migration pattern changes slightly but the underlying prep work from Steps 1 through 6 still applies. Kubernetes already treats architecture as a schedulable dimension through node labels, so the cleanest approach is running a mixed-architecture cluster during the transition rather than a hard cutover.
eksctl create nodegroup \
--cluster prod-cluster \
--name graviton5-ng \
--node-type m9g.xlarge \
--nodes 3 \
--nodes-min 3 \
--nodes-max 10 \
--node-labels "kubernetes.io/arch=arm64,workload-tier=graviton5"
# 2026-08-15 10:22:04 [ℹ] nodegroup "graviton5-ng" has 3 node(s)
# 2026-08-15 10:22:04 [ℹ] waiting for at least 3 node(s) to become ready
# 2026-08-15 10:24:11 [✔] created nodegroup "graviton5-ng" in cluster "prod-cluster"
Once the node group is ready, add a nodeAffinity or nodeSelector rule to a non-critical deployment first and confirm the pod schedules correctly and the multi-arch image (built in Step 5) pulls the right manifest layer automatically:
spec:
template:
spec:
nodeSelector:
kubernetes.io/arch: arm64
containers:
- name: api
image: your-account.dkr.ecr.us-east-1.amazonaws.com/api-service:v2.4.0
Scale the Graviton5 node group up and the x86 node group down gradually, the same weighted-rollout logic from Step 10 applied at the Kubernetes scheduler level instead of the load balancer level. Once the x86 node group is idle, cordon it, drain it, and delete it from your cluster configuration.
IAM Permissions and Security Considerations
A Graviton5 migration doesn’t require new IAM permission models, since the instance-type change itself is invisible to IAM policies written against resource ARNs rather than instance types. That said, a few permission and security gaps show up consistently during this kind of migration and are worth checking explicitly rather than assuming they’ll surface on their own.
- Compute Optimizer and Cost Explorer read access: the IAM role or user running Steps 2 and 12 needs
compute-optimizer:Get*andce:GetCostAndUsagepermissions specifically. These aren’t included in a typicalAmazonEC2FullAccesspolicy and have to be added separately. - CI/CD role scope for ECR pushes: the role assumed by your GitHub Actions or CodeBuild pipeline needs
ecr:PutImageandecr:BatchCheckLayerAvailabilityfor the multi-arch push in Step 6. A role scoped only to pulling images will fail silently on the push step with an access-denied error that’s easy to misread as a Docker configuration issue. - Launch template IAM instance profiles carry over unchanged: the instance profile attached to your launch template doesn’t need modification for the architecture change itself, but audit it anyway while you’re touching the template, since migrations are a natural checkpoint to remove overly broad permissions that accumulated over time.
- IMDSv2 enforcement: if you haven’t already enforced IMDSv2-only (token-based) metadata requests account-wide, a Graviton5 migration is a reasonable moment to add
HttpTokens: requiredto your launch template, since you’re already touching it and validating application behavior end to end in Step 11.
Instance Family Reference: What Maps to What
Use this as a quick lookup when you’re deciding which Graviton5 family replaces which instance in your current fleet. Full specs for each size are published on AWS’s M9g instance type page.
| Current instance type | Architecture | Graviton5 replacement | Notes |
|---|---|---|---|
| m6i, m7i, m5 | x86 | M9g / M9gd | General purpose, requires arm64 rebuild |
| c6i, c7i, c5 | x86 | C9g / C9gd | Compute-optimized, PCIe Gen 6 on Nitro |
| r6i, r7i, r5 | x86 | R9g / R9gd | Memory-optimized, up to 25% gain vs R8g |
| m8g | arm64 (Graviton4) | M9g / M9gd | Same-architecture swap, low risk |
| c8g | arm64 (Graviton4) | C9g / C9gd | Same-architecture swap, low risk |
| r8g | arm64 (Graviton4) | R9g / R9gd | Same-architecture swap, low risk |
| x8g, i8g | arm64 (Graviton4) | No direct Graviton5 equivalent yet | Stay on Graviton4 for high-memory/storage-optimized shapes |
Common Pitfalls When Migrating to Graviton5
- Assuming region availability matches Graviton4’s footprint: Graviton5 launched in only four regions (us-east-1, us-east-2, us-west-2, eu-central-1) as of this writing, versus the much broader Graviton4 coverage. Check region availability before you plan a multi-region rollout.
- Skipping the arm64 dependency audit: teams that jump straight to launching instances without checking native extensions and commercial agents lose days debugging cryptic segfaults that trace back to an x86-only binary silently failing under emulation or refusing to load.
- Forgetting QEMU in CI: amd64 GitHub Actions runners can’t build arm64 images without an emulation layer registered first. The build fails with an exec-format error that doesn’t obviously point at the real cause.
- Treating a synthetic benchmark as the final answer: sysbench numbers are useful for a sanity check, but they don’t capture your actual query patterns, garbage collection behavior, or I/O profile. Always validate against real or replayed production traffic.
- Big-bang cutovers on stateful services: flipping a database instance class without a maintenance window and rollback plan turns a routine migration into an incident. Schedule these separately from stateless compute.
- Not updating IMDSv2 tooling before launch: if your account enforces token-based instance metadata and a sidecar agent still expects the older IMDSv1 pattern, new instances will fail health checks in a way that looks unrelated to the architecture change.
Troubleshooting Guide
- “exec format error” when running a container: the image manifest doesn’t include an arm64 layer for the tag you pulled. Rebuild with
docker buildx build --platform linux/amd64,linux/arm64and confirm the push succeeded withdocker buildx imagetools inspect. - Instance fails to launch with an AMI compatibility error: you’re pointing at an x86-only AMI. Find the arm64 variant of the same OS release. Amazon Linux 2023, Ubuntu 24.04, and Debian 13 all publish separate AMI IDs per architecture.
- pip install fails on a package with C extensions: the package doesn’t publish a prebuilt arm64 wheel for your Python version. Check PyPI directly, or fall back to building from source with the required system headers installed via apt/yum first.
- CI build hangs or fails only on the arm64 target: confirm
docker/setup-qemu-action(GitHub Actions) or the equivalent emulation setup runs before the build step. Without it, cross-architecture builds silently stall or error out. - Application starts but throws unexpected floating-point or byte-order bugs: rare, but some legacy code makes little-endian or x86-specific assumptions. arm64 is little-endian too, so true byte-order bugs are uncommon. More often this is an uninitialized-memory bug that happened to be masked by x86 behavior and is now visible.
- Auto Scaling Group won’t launch Graviton5 capacity: check that the launch template’s AMI is arm64-compatible and that the target region actually has Graviton5 capacity. A mixed-instances policy silently falls back to other listed types if the top choice can’t launch.
- Aurora/RDS modify-instance-class fails or isn’t offered: not every engine version has a Graviton5-class instance type published yet. Check the RDS console’s instance class dropdown for your specific engine and version before scheduling the maintenance window.
- Load balancer health checks fail after cutover but the app is running: check IMDSv2 token requirements and security group rules first. These are the two most common causes of a healthy-app-but-unhealthy-target-group state after an instance-type change.
- Costs didn’t drop as expected after migration: pull the Cost Explorer instance-type breakdown from Step 12 and check for orphaned old instances, unattached EBS volumes from terminated instances, or a mismatch between reserved/savings plan coverage and the new instance family.
Advanced Tips: Getting More Out of Graviton5
Once the basic migration is stable, a few architecture-specific tuning steps can squeeze out additional performance that a straight lift-and-shift leaves on the table.
- Recompile with architecture-specific flags: for compiled languages (C, C++, Rust, Go with cgo), rebuilding with
-march=armv9-aor the equivalent target flag lets the compiler use instructions specific to the Graviton5 core rather than a generic arm64 baseline. - Use Amazon Corretto for Java workloads: Amazon’s OpenJDK distribution is tuned and tested specifically against Graviton hardware, and AWS publishes Graviton-specific JVM tuning guidance for garbage collector and heap settings.
- Favor musl-based or slim base images where compatible: smaller base images reduce cold-start time on Lambda and container platforms, and arm64 builds of Alpine-based images are now broadly available for common runtimes.
- Right-size before you migrate, not after: since core counts and cache sizes differ meaningfully between generations, an instance size that was correctly sized for a Graviton4 workload may be oversized (or undersized) on Graviton5. Re-run Compute Optimizer after a soak period rather than assuming a 1:1 size mapping.
- Pin NVMe-backed variants (M9gd, C9gd, R9gd) for local scratch space: if your workload does heavy temp-file I/O (build caches, video transcoding, ML data loading), the local NVMe variants avoid EBS latency entirely for that scratch data.
Complete Working Project: A Multi-Arch API Deployment
Putting the pieces from this tutorial together, here’s a minimal but complete project structure that deploys a containerized API to both x86 and Graviton5 targets behind a single Auto Scaling Group, so you can see the full pattern in one place.
project/
├── Dockerfile # single Dockerfile, no arch-specific branches needed
├── .github/workflows/build.yml # multi-arch build from Step 6
├── terraform/
│ ├── main.tf # ASG + mixed_instances_policy from Step 7
│ ├── launch_template.tf
│ ├── variables.tf
│ └── outputs.tf
└── scripts/
├── benchmark.sh # sysbench wrapper from Step 8
└── rollout.sh # weighted target group shifter from Step 10
A minimal Dockerfile that works unmodified across both architectures, since Buildx handles the platform-specific base image resolution automatically:
FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Because the official Node.js image publishes both amd64 and arm64 manifests under the same tag, this Dockerfile needs zero changes to run correctly on an M9g instance. That’s the practical payoff of doing the migration properly: once the base images, CI pipeline, and Terraform are updated, day-to-day application code doesn’t need to know or care which architecture it’s running on.
Wire the pieces together in this order for a clean rollout: build and push the multi-arch image (Steps 5–6), deploy the updated launch template and ASG mixed instances policy (Step 7), launch a small percentage of Graviton5 capacity and benchmark it against the existing fleet (Step 8), then use the weighted target group script (Step 10) to shift traffic gradually while watching CloudWatch dashboards for latency and error-rate regressions.
How Graviton5 Compares to Sticking With x86
For teams weighing whether this migration is worth the engineering time, the honest answer depends on workload shape more than raw specs. CPU-bound, horizontally scaled services (API layers, worker queues, batch processing) tend to see the clearest wins, since they can take advantage of the higher core count and larger cache directly. Workloads with heavy dependence on a specific x86 instruction set extension, licensed software without an arm64 build, or a single legacy binary nobody wants to touch are the ones where the cost of migration still outweighs the benefit today.
Recently published on-demand pricing puts m9g.large at $0.0980 per hour, reported as roughly 2.8% cheaper than the equivalent Graviton4 general-purpose size, while m9gd.large (with local NVMe storage) runs $0.1260 per hour. That’s a modest per-hour delta on its own, but combined with the throughput gains from the larger core count and cache, the effective cost-per-request drop tends to be larger than the sticker-price difference suggests, which is exactly the kind of thing Step 8’s benchmarking phase is designed to quantify for your specific workload rather than a generic rate card.
Frequently Asked Questions
Is AWS Graviton5 available in every AWS region?
No. As of this writing, M9g/M9gd, C9g/C9gd, and R9g/R9gd are available in us-east-1, us-east-2, us-west-2, and eu-central-1, with AWS stating additional regions are planned. Check the EC2 console’s instance type availability for your specific region before planning a rollout.
Do I need to rewrite my application to run on Graviton5?
Usually no, if your application is written in a language with native arm64 support (Node.js, Python, Java, Go, Rust) and doesn’t depend on x86-only binaries. Most of the migration work is in the build pipeline and dependency verification, not application code changes.
Should I migrate directly from x86 to Graviton5, or go through Graviton4 first?
Go straight to Graviton5 if you’re migrating from x86 today. There’s no technical benefit to landing on Graviton4 first. If you’re already on Graviton4, the Graviton4-to-Graviton5 move is a same-architecture instance-type swap with substantially lower risk than an x86 migration.
Will my existing Graviton4 AMIs work on Graviton5 instances?
In most cases yes, since both are arm64 and AWS maintains compatibility across Graviton generations for standard Linux distributions. Still test in a non-production environment first, since custom kernel modules or hardware-specific drivers built against Graviton4 aren’t guaranteed to behave identically.
What happens to my Graviton4 Reserved Instances or Savings Plans if I migrate?
Instance-family Reserved Instances are tied to the specific family you purchased and don’t automatically apply to Graviton5 families. Compute Savings Plans, by contrast, apply across instance families and architectures, so accounts using Savings Plans have more flexibility to shift generations without losing committed-use discounts.
How long should a Graviton5 migration take for a typical production service?
For a single containerized service with a modern CI/CD pipeline, expect one to two weeks: a few days for dependency auditing and CI updates, a few days of benchmarking and staged rollout, and a monitoring buffer before decommissioning old capacity. Stateful services and large monoliths take longer.
Does Kubernetes handle mixed Graviton5 and x86 node pools automatically?
Yes, if your container images are multi-arch (built with Buildx as shown in Step 5). Kubernetes schedules pods based on the kubernetes.io/arch node label, and the container runtime pulls the correct manifest layer automatically, so a single deployment manifest can span mixed-architecture node pools without modification.
Is there a Graviton5 equivalent for storage-optimized or high-memory instances yet?
Not as of this writing. X8g (high-memory) and I8g (storage-optimized) remain on Graviton4 with no announced Graviton5 successor yet, so workloads needing those specific shapes should stay on Graviton4 until AWS extends the Graviton5 lineup further.


