AWS Graviton instances (ARM) cost 20% less than x86 and often perform better. Here's how to migrate with Terraformβusually just changing t3 to t4g.
Real talk: Are you still using t3, m5, or c5 instances?
You're overpaying. By at least 20%.
AWS Graviton instances (ARM-based) cost significantly less and often perform better than equivalent x86 instances:
t3.large: $0.0832/hour = $60.74/month
t4g.large: $0.0672/hour = $49.06/month
Difference: $11.68/month = $140/year
Per instance. Multiply by your fleet size.
The migration? Change t3 to t4g in your Terraform. That's it. π
Let me show you how.
π° The Graviton Advantage
AWS Graviton processors (ARM) offer:
- 20-40% better price-performance than x86
- Up to 40% better performance for many workloads
- 60% less energy consumption (bonus: eco-friendly!)
Pricing comparison:
| Instance Type | x86 ($/hour) | Graviton ($/hour) | Savings |
|---|---|---|---|
| t3.medium β t4g.medium | $0.0416 | $0.0336 | 19% |
| t3.large β t4g.large | $0.0832 | $0.0672 | 19% |
| t3.xlarge β t4g.xlarge | $0.1664 | $0.1344 | 19% |
| m5.large β m6g.large | $0.096 | $0.077 | 20% |
| c5.large β c6g.large | $0.085 | $0.068 | 20% |
| r5.large β r6g.large | $0.126 | $0.1008 | 20% |
Pattern: Replace the 5 with 6g or 3 with 4g. Save 20%.
π― Who Should Migrate?
Perfect candidates:
- β Web servers (Nginx, Apache)
- β Application servers (Node.js, Python, Go, Ruby)
- β Containerized workloads (Docker, ECS, EKS)
- β CI/CD runners
- β Databases (Postgres, MySQL, Redis work great on ARM)
- β Caching layers (Redis, Memcached)
Requires testing:
- β οΈ Legacy Java apps (usually fine, but test)
- β οΈ .NET Framework (use .NET Core instead)
- β οΈ Compiled binaries without ARM support
Not compatible (yet):
- β Windows workloads (x86 only)
- β Software with x86-only dependencies
π οΈ Terraform Migration
Single Instance (Simplest)
Before:
resource "aws_instance" "app" {
ami = "ami-0c55b159cbfafe1f0" # x86 AMI
instance_type = "t3.large"
tags = {
Name = "app-server"
}
}
After:
resource "aws_instance" "app" {
ami = "ami-0d5eff06f840b45e9" # ARM AMI
instance_type = "t4g.large" # Changed from t3
tags = {
Name = "app-server"
}
}
Deploy:
terraform apply
# EC2 will be recreated with Graviton instance
# Annual savings: $140 per instance
Auto Scaling Group (Production-Ready)
Before:
resource "aws_launch_template" "app" {
name_prefix = "app-"
image_id = data.aws_ami.amazon_linux_2_x86.id
instance_type = "t3.large"
# ... other config
}
data "aws_ami" "amazon_linux_2_x86" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-gp2"]
}
}
After:
resource "aws_launch_template" "app" {
name_prefix = "app-"
image_id = data.aws_ami.amazon_linux_2_arm.id
instance_type = "t4g.large" # Changed from t3
# ... other config
}
data "aws_ami" "amazon_linux_2_arm" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm-*-arm64-gp2"] # Changed to arm64
}
}
Multi-Architecture Module
# modules/ec2-instance/main.tf
variable "instance_family" {
description = "Instance family (t3, t4g, m5, m6g, etc.)"
type = string
default = "t4g" # Default to Graviton
}
variable "instance_size" {
description = "Instance size (medium, large, xlarge)"
type = string
}
locals {
# Determine architecture from instance family
is_graviton = can(regex(".*g$", var.instance_family))
architecture = local.is_graviton ? "arm64" : "x86_64"
}
data "aws_ami" "amazon_linux_2" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm-*-${local.architecture}-gp2"]
}
filter {
name = "architecture"
values = [local.architecture]
}
}
resource "aws_instance" "this" {
ami = data.aws_ami.amazon_linux_2.id
instance_type = "${var.instance_family}.${var.instance_size}"
tags = {
Name = "instance-${var.instance_family}"
Architecture = local.architecture
}
}
output "instance_id" {
value = aws_instance.this.id
}
output "architecture" {
value = local.architecture
}
Usage:
# main.tf
# Graviton instance (default)
module "app_server" {
source = "./modules/ec2-instance"
instance_family = "t4g"
instance_size = "large"
}
# x86 instance (if needed for compatibility)
module "legacy_server" {
source = "./modules/ec2-instance"
instance_family = "t3"
instance_size = "large"
}
ECS/EKS Migration
ECS on EC2:
# Just change the instance type in launch template
resource "aws_launch_template" "ecs" {
name_prefix = "ecs-"
image_id = data.aws_ami.ecs_optimized_arm.id
instance_type = "t4g.large" # Changed from t3
user_data = base64encode(<<-EOF
#!/bin/bash
echo ECS_CLUSTER=${aws_ecs_cluster.main.name} >> /etc/ecs/ecs.config
EOF
)
}
data "aws_ami" "ecs_optimized_arm" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-ecs-hvm-*-arm64-ebs"]
}
}
EKS Node Group:
resource "aws_eks_node_group" "graviton" {
cluster_name = aws_eks_cluster.main.name
node_group_name = "graviton-nodes"
node_role_arn = aws_iam_role.node.arn
subnet_ids = var.private_subnet_ids
instance_types = ["t4g.large"] # Graviton
scaling_config {
desired_size = 3
max_size = 5
min_size = 1
}
# EKS automatically uses correct AMI for architecture
}
π Real-World Savings
Example 1: Web Application (10 instances)
Before (t3.large):
10 Γ t3.large Γ $0.0832/hour Γ 730 hours = $607/month
Annual: $7,284
After (t4g.large):
10 Γ t4g.large Γ $0.0672/hour Γ 730 hours = $490/month
Annual: $5,886
Savings: $117/month = $1,398/year (19% reduction!)
Example 2: Microservices (50 instances)
Before (mix of t3/m5):
30 Γ t3.xlarge = $365/month
20 Γ m5.large = $140/month
Total: $505/month
Annual: $6,060
After (Graviton):
30 Γ t4g.xlarge = $295/month
20 Γ m6g.large = $112/month
Total: $407/month
Annual: $4,884
Savings: $98/month = $1,176/year (19% reduction!)
Example 3: Kubernetes Cluster (100 nodes)
Before (m5.2xlarge nodes):
100 Γ m5.2xlarge Γ $0.384/hour Γ 730 hours = $28,032/month
Annual: $336,384
After (m6g.2xlarge):
100 Γ m6g.2xlarge Γ $0.308/hour Γ 730 hours = $22,484/month
Annual: $269,808
Savings: $5,548/month = $66,576/year (20% reduction!) π°
π Migration Checklist
Phase 1: Planning (1 hour)
- [ ] Audit current instance types
- [ ] Check application compatibility (most apps just work)
- [ ] Identify AMI requirements (arm64 vs x86_64)
- [ ] Plan testing approach
Phase 2: Testing (1-2 days)
- [ ] Deploy test instance with Graviton
- [ ] Run application tests
- [ ] Performance benchmarking
- [ ] Verify all dependencies work
Phase 3: Migration (varies)
- [ ] Update Terraform configuration
- [ ] Deploy to dev environment
- [ ] Deploy to staging
- [ ] Deploy to production (rolling update)
- [ ] Monitor performance and costs
π‘ Pro Tips
1. Use Latest Amazon Linux 2 AMIs
data "aws_ami" "amazon_linux_2_arm" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm-*-arm64-gp2"]
}
filter {
name = "state"
values = ["available"]
}
}
Amazon Linux 2 has excellent ARM support out of the box.
2. Docker Multi-Arch Builds
# Build for both architectures
FROM --platform=$BUILDPLATFORM node:18-alpine AS builder
# Your app code
COPY . .
RUN npm ci && npm run build
FROM node:18-alpine
COPY --from=builder /app/dist ./dist
CMD ["node", "dist/index.js"]
# Build multi-arch image
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest .
3. Start with Non-Critical Workloads
- Dev/test environments first
- CI/CD runners
- Non-customer-facing services
- Then move to production
4. Monitor Performance
resource "aws_cloudwatch_metric_alarm" "cpu" {
alarm_name = "graviton-high-cpu"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 2
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = 300
statistic = "Average"
threshold = 80
dimensions = {
InstanceId = aws_instance.app.id
}
}
5. Use Graviton for RDS Too
resource "aws_db_instance" "postgres" {
engine = "postgres"
engine_version = "15.3"
instance_class = "db.t4g.large" # Graviton for RDS!
allocated_storage = 100
storage_type = "gp3"
}
RDS Graviton saves 20% on database costs too!
β οΈ Common Gotchas
1. AMI Must Match Architecture
# WRONG - x86 AMI with Graviton instance
instance_type = "t4g.large"
ami = "ami-xxx" # x86 AMI = fails
# RIGHT - ARM AMI with Graviton instance
instance_type = "t4g.large"
ami = "ami-yyy" # arm64 AMI = works
2. Check Your Container Images
# Verify multi-arch support
docker manifest inspect nginx:latest
# Should show both architectures:
# - linux/amd64
# - linux/arm64
3. Some Software Requires Recompilation
If you compile custom software, ensure ARM toolchain:
# Use correct compiler
gcc -march=armv8-a # For ARM
# vs
gcc -march=x86-64 # For x86
π Quick Start
# 1. Find current instance types
terraform state list | grep aws_instance
# 2. Update instance_type
# Change: t3 β t4g, m5 β m6g, c5 β c6g
# 3. Update AMI to arm64
# Change AMI filter to include "arm64"
# 4. Test in dev
terraform plan
terraform apply
# 5. Verify it works
ssh user@instance
uname -m # Should show "aarch64"
# 6. Roll out to production
# Use rolling update for zero downtime
# 7. Watch savings accumulate π°
π― Quick Reference
Instance Type Mapping
| x86 Instance | Graviton Equivalent | Savings |
|---|---|---|
| t3.nano | t4g.nano | 20% |
| t3.micro | t4g.micro | 20% |
| t3.small | t4g.small | 20% |
| t3.medium | t4g.medium | 19% |
| t3.large | t4g.large | 19% |
| t3.xlarge | t4g.xlarge | 19% |
| t3.2xlarge | t4g.2xlarge | 19% |
| m5.large | m6g.large | 20% |
| m5.xlarge | m6g.xlarge | 20% |
| c5.large | c6g.large | 20% |
| r5.large | r6g.large | 20% |
AMI Filters
# Amazon Linux 2
filter {
name = "name"
values = ["amzn2-ami-hvm-*-arm64-gp2"]
}
# Ubuntu
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-*-arm64-server-*"]
}
# ECS Optimized
filter {
name = "name"
values = ["amzn2-ami-ecs-hvm-*-arm64-ebs"]
}
π― Summary
The Opportunity:
- 20% instant savings on EC2 costs
- Often better performance
- Lower energy consumption
The Migration:
- Change instance type: t3 β t4g
- Update AMI to arm64
- Test and deploy
The Result:
- Typical savings: $1,000-60,000/year
- Implementation time: Hours to days
- Ongoing benefit: Forever
Stop overpaying for x86 instances. Switch to Graviton and save 20% instantly. πͺ
Migrated to Graviton? How much are you saving? Share in the comments! π¬
Follow for more AWS cost optimization with Terraform! π
Top comments (0)