Graviton offers a meaningful price-performance improvement for most workloads, which makes it one of the few optimisations that is genuinely close to free. 'Close to' is doing some work in that sentence. Here is what a forty-service migration actually involved.
The short version
Thirty-six of forty services moved with a base image change, a rebuild and a redeploy. Effort for those was measured in hours, mostly spent waiting for pipelines. Four required real work, and one of those we abandoned.
Aggregate compute cost for the migrated services fell around twenty-three per cent at equivalent throughput, with a modest latency improvement on the JVM services.
What moved without incident
- Every Go service — cross-compilation is a build flag
- Every Node.js service on a current runtime
- JVM services on JDK 17 and later, several of which got slightly faster
- Python services whose dependencies were pure Python or had ARM wheels published
For this category the migration was genuinely a base image change. The multi-architecture build was more work to set up than any individual service was to move.
What resisted
1. A Python service pinned to an old numerical library
A dependency pinned four years ago with no ARM wheel available. Building from source on ARM worked but produced different floating-point results in the last decimal place — irrelevant mathematically, sufficient to fail a snapshot test suite that asserted exact string equality on formatted output.
The real problem was the test suite, not the architecture. We fixed the assertions.
2. A container with an x86 binary baked in
A vendor-supplied CLI tool copied into the image during build, with no ARM distribution. We moved that single step into a sidecar on x86 and left the main service on ARM.
3. A service depending on a native library with hand-written assembly
A compression library with x86 SIMD paths and a much slower generic fallback on ARM. Migrating would have been a net performance loss, so we left it where it was. Not every service should move.
4. The one we abandoned
A legacy service with a compiled extension whose source we could not locate. We had already scheduled it for decommissioning; spending a week on it to save a small monthly amount for six months was not defensible.
The correct Graviton coverage number is not one hundred per cent. It is 'everything where the migration costs less than it saves'.
How to sequence it
- Set up multi-architecture builds first. Doing this once removes most of the friction from every subsequent service.
- Start with a mid-tier service, not the smallest. The smallest service will not surface the dependency problems, so you learn nothing from it.
- Run both architectures side by side behind a load balancer. Compare latency and error rates on real traffic before shifting fully.
- Keep an x86 node pool. You will need it for the services that should not move, and pretending otherwise creates pressure to force bad migrations.
Inventory your dependencies for native extensions before planning anything. Ten minutes with your lock files will tell you which services are in the easy 90% and which need a conversation.
Was it worth it?
For a forty-service estate, yes — roughly a quarter off compute cost for a few days of engineering, most of it one-time platform work. For a five-service estate the platform setup would dominate and the answer is less obvious.
The other benefit is less quantifiable: the multi-architecture build capability makes every future architecture decision cheaper, which is worth something on its own.
Considering a Graviton program?
We can run the dependency inventory and give you the realistic coverage number before you commit engineering time to it.