26.7 ECS on AWS Graviton: ARM-Based Cost Savings
Right, so you’ve decided to run your containers on ECS. Good choice. It’s a solid system once you wrestle it into submission. Now, let’s talk about saving money without sacrificing performance, because who doesn’t like keeping their CFO (or their own wallet) happy? Enter AWS Graviton2 and Graviton3 processors. These are AWS’s own ARM-based silicon, and they’re not some gimmick—they offer significant price-performance benefits over the equivalent x86 instances. We’re talking about 20-40% better performance for the same cost or, more commonly, the same performance for 20-40% less cost. I’ll wait while you do a little happy dance.
The beautiful part is that for once, leveraging this isn’t some arcane, painful process. It’s almost comically simple, which is a nice change of pace. The core concept is this: your task definition’s runtimePlatform setting and your underlying container image’s architecture must match. You can’t run an amd64 (x86) image on an arm64 (ARM) CPU. It’s like trying to fit a square peg in a round hole, if the peg and hole were made of ones and zeroes and the peg just throws a Segmentation Fault and gives up.
The Two Pillars of Graviton Success
There are two non-negotiable things you must get right. Screw up either one, and your task will either fail to launch or, worse, you’ll get billed for an x86 instance without realizing it.
First, your container image must be built for the arm64 architecture. This isn’t an AWS thing; it’s a processor instruction set thing. If you’re using a public image, check its manifest. Many major images now are multi-arch, meaning they support amd64, arm64, and sometimes others. For your own images, you need to build them correctly.
# Your Dockerfile doesn't change much, but *how* you build it does.
# You can build for arm64 on your amd64 laptop using buildx.
docker buildx build --platform linux/arm64 -t my-repo/my-app:arm-latest .
The second pillar is your ECS Task Definition. This is where you declare your intent to use ARM hardware. You do this in the runtimePlatform section. If you skip this, ECS will default to amd64 and, if you’re lucky, your task will fail immediately. If you’re unlucky, it might pull an x86 image that happens to exist and run it on a more expensive instance.
{
"family": "my-graviton-task",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"runtimePlatform": {
"cpuArchitecture": "ARM64",
"operatingSystemFamily": "LINUX"
},
"cpu": "1024",
"memory": "2048",
"containerDefinitions": [
{
"name": "my-app",
"image": "my-repo/my-app:arm-latest", // This MUST be an ARM image
"essential": true,
"portMappings": [{"containerPort": 8080}]
}
]
}
The Quiet Pitfall: The “Default” Image Tag
Here’s the bit that trips up a lot of very smart people. You’re using a public image, say nginx:latest. You wisely set "cpuArchitecture": "ARM64" in your task definition. It works! Hooray! You assume nginx:latest is a multi-arch image that supports ARM… and you’re right. Today.
But what if the maintainer of that image changes the :latest tag tomorrow to point to an experimental build that’s only for amd64? Your service, which was working perfectly, will suddenly fail to deploy new tasks. The fix is simple but non-optional: pin your images to a specific digest or a tag that explicitly denotes architecture. Don’t rely on the vagaries of a floating tag.
// Good (pinned by digest, the most stable)
"image": "nginx@sha256:...",
// Also Good (explicit architecture tag)
"image": "nginx:1.23-alpine",
// Asking for trouble
"image": "nginx:latest"
Fargate and Graviton: A Match Made in Cloud Heaven
This is where Graviton truly shines. With Fargate, you don’t pick an instance type; you pick CPU and memory. And guess what? The Graviton price is automatically applied when you specify the ARM64 architecture. There’s no separate SKU to find. You specify "runtimePlatform": {"cpuArchitecture": "ARM64"}, and the Fargate pricing calculator flips to the cheaper, Graviton rate. It’s the easiest cost savings you’ll ever implement.
The vCPU and memory amounts are the same (256, 512, 1024, etc.), but the cost per unit is lower. So a 1 vCPU/2GB Fargate task on Graviton costs noticeably less than the same-sized task on x86. The performance is, in my experience and in every benchmark AWS proudly crows about, equivalent or better. It’s a no-brainer.
When to Stick with x86 (For Now)
Graviton is brilliant, but it’s not magic. There are still a few holdouts. If your application or any of its dependencies include native binaries (e.g., certain database clients, machine learning libraries, legacy system tools) that were only compiled for amd64, you’ll have to stick with x86 until those components get ARM support. The quickest way to test is to pull your image onto a Graviton-based EC2 instance (a t4g.small is perfect and cheap) and run it there. If it runs, it’ll run on Fargate.
The bottom line? Unless you have a hard dependency blocking you, you should be using Graviton. It’s not a fringe option anymore; it’s the default choice for cost-conscious, performance-aware engineers. And anyone who tells you otherwise is probably still compiling their code on an Intel machine from 2015.