DEV Community

Cover image for Change 6 Characters in Terraform and Save 20% on EC2 Costs Instantly πŸ’ͺ
Suhas Mallesh
Suhas Mallesh

Posted on • Edited on

Change 6 Characters in Terraform and Save 20% on EC2 Costs Instantly πŸ’ͺ

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.
Enter fullscreen mode Exit fullscreen mode

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"
  }
}
Enter fullscreen mode Exit fullscreen mode

After:

resource "aws_instance" "app" {
  ami           = "ami-0d5eff06f840b45e9"  # ARM AMI
  instance_type = "t4g.large"  # Changed from t3

  tags = {
    Name = "app-server"
  }
}
Enter fullscreen mode Exit fullscreen mode

Deploy:

terraform apply

# EC2 will be recreated with Graviton instance
# Annual savings: $140 per instance
Enter fullscreen mode Exit fullscreen mode

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"]
  }
}
Enter fullscreen mode Exit fullscreen mode

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
  }
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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"]
  }
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

πŸ“Š 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
Enter fullscreen mode Exit fullscreen mode

After (t4g.large):

10 Γ— t4g.large Γ— $0.0672/hour Γ— 730 hours = $490/month
Annual: $5,886

Savings: $117/month = $1,398/year (19% reduction!)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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!)
Enter fullscreen mode Exit fullscreen mode

Example 3: Kubernetes Cluster (100 nodes)

Before (m5.2xlarge nodes):

100 Γ— m5.2xlarge Γ— $0.384/hour Γ— 730 hours = $28,032/month
Annual: $336,384
Enter fullscreen mode Exit fullscreen mode

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!) πŸ’°
Enter fullscreen mode Exit fullscreen mode

πŸŽ“ 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"]
  }
}
Enter fullscreen mode Exit fullscreen mode

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"]
Enter fullscreen mode Exit fullscreen mode
# Build multi-arch image
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest .
Enter fullscreen mode Exit fullscreen mode

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
  }
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

2. Check Your Container Images

# Verify multi-arch support
docker manifest inspect nginx:latest

# Should show both architectures:
# - linux/amd64
# - linux/arm64
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

πŸš€ 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 πŸ’°
Enter fullscreen mode Exit fullscreen mode

🎯 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"]
}
Enter fullscreen mode Exit fullscreen mode

🎯 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)