Files
Hugo/content/blog/terraform-aws-ecs.md
T
fukunee 1fb7959071 Standardize poet attributions and reformat blog template
Convert all poet attribution lines to use an em dash, English
name format, and consistent spacing. Re-indent the blog single
page template for readability and adjust the typewriter element
layout.
2026-05-14 02:29:10 +08:00

77 lines
2.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: "Deploying to AWS ECS with Terraform"
date: 2026-05-14T10:00:00+08:00
draft: false
poet: |
If you have built castles in the air,
your work need not be lost;
that is where they should be.
Now put the foundations under them.
— Henry David Thoreau, US, 1817–1862
description: "Infrastructure as code from scratch: VPC, subnets, security groups, ECR repository, ECS service with Fargate, and an ALB in front. Every resource explained."
tags:
- AWS
- Terraform
- DevOps
---
ECS with Fargate is the sweet spot between managing servers and going full serverless. Terraform makes it reproducible.
## The Stack
Here's what we need to provision:
| Resource | Purpose |
|----------|---------|
| VPC | Isolated network |
| Public & private subnets | Multi-AZ placement |
| Security groups | Network access control |
| ECR repository | Container image storage |
| ECS cluster + service | Run containers |
| ALB + target group | Traffic distribution |
## Security Groups: Getting Them Right
The key is getting the security groups right — one misconfigured rule and your containers can't talk to each other, or worse, your database is exposed to the internet.
```hcl
resource "aws_security_group" "ecs_service" {
name = "ecs-service-sg"
vpc_id = aws_vpc.main.id
ingress {
from_port = 3000
to_port = 3000
protocol = "tcp"
security_groups = [aws_security_group.alb.id]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
```
The principle: **allow the minimum**. The ALB security group allows inbound from the internet on 80/443. The ECS security group only allows inbound from the ALB. The database security group only allows inbound from ECS.
## Fargate Configuration
Fargate abstracts the EC2 instances. You specify CPU and memory, and AWS handles the rest. For most web services, 0.25 vCPU / 512 MB is a good starting point — scale up based on load testing, not guessing.
## CI/CD Integration
After Terraform creates the infrastructure, your CI pipeline needs to:
1. Build and tag the Docker image
2. Push to ECR
3. Update the ECS service with the new task definition
Use a Terraform backend with state locking — S3 + DynamoDB is the standard approach. Without state locking, two people running `terraform apply` at the same time can corrupt your infrastructure state.
## Cost Notes
Fargate is more expensive per vCPU-hour than raw EC2, but you save the operational cost of patching instances and managing capacity. For small-to-medium workloads, the difference is negligible compared to the engineering time saved.