Efficient Container Deployment to AWS: ECS + Fargate
Many teams bog down deployments by maintaining EC2-based clusters, wrestling with patching cycles and scaling headaches. In practice, ECS with Fargate offloads this entire class of problem—no EC2 fleet, just containers as an isolated, managed service.
Problem: How to Ship Containers Without Infrastructure Overhead
High-velocity software teams need rapid deployment, horizontal scaling, and zero interest in managing OS lifecycles. ECS + Fargate targets this with a serverless container runtime—provision once, scale automatically, limit blast radius at the task level.
Below: a proven ECS/Fargate pipeline, used in production, minimizes manual steps and infrastructure sprawl. No hand-edited launch scripts, no SSH into hosts.
Container Image: Build and Push to ECR
Non-negotiable: Docker images must reside in a registry accessible to ECS. Amazon ECR (Elastic Container Registry) supports fine-grained IAM policies and lifecycle rules—important if you iterate quickly.
Workflow: Bash (tested with AWS CLI v2, Docker 20.10+)
# 1. Create an ECR repo. Change region as needed.
aws ecr create-repository --repository-name my-app --region us-east-1
# 2. Log in to ECR (token expires in 12 hours)
aws ecr get-login-password --region us-east-1 \
| docker login --username AWS --password-stdin <aws_account_id>.dkr.ecr.us-east-1.amazonaws.com
# 3. Build and tag your image
docker build --platform linux/amd64 -t my-app:2024-06-10 .
docker tag my-app:2024-06-10 <aws_account_id>.dkr.ecr.us-east-1.amazonaws.com/my-app:2024-06-10
# 4. Push
docker push <aws_account_id>.dkr.ecr.us-east-1.amazonaws.com/my-app:2024-06-10
Note: Tag with a date or Git SHA for traceability. “latest” is not recommended in CI systems.
Known issue: Insufficient IAM permissions for ECR will fail authentication—error:
denied: User is not authorized to perform: ecr:BatchCheckLayerAvailability
Cluster: ECS Logical Group
No EC2, but you still declare a cluster for logical grouping and isolation (metrics, scaling).
aws ecs create-cluster --cluster-name my-fargate-cluster
Nothing to patch, nothing to monitor from a compute perspective. Cluster merely groups task deployments.
Task Definition: JSON Spec for Workload
Task definitions are the crux—specify image, resource requests, network mode, environment variables. Fargate mandates networkMode: awsvpc
(each task gets its own ENI).
Minimal task definition (ECS Fargate):
{
"family": "my-app-task",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "256",
"memory": "512",
"containerDefinitions": [{
"name": "my-app-container",
"image": "<aws_account_id>.dkr.ecr.us-east-1.amazonaws.com/my-app:2024-06-10",
"portMappings": [{
"containerPort": 8080,
"protocol": "tcp"
}],
"environment": [
{"name": "APP_ENV", "value": "production"}
],
"essential": true
}]
}
aws ecs register-task-definition --cli-input-json file://task-definition.json
Gotcha: For Fargate, possible CPU/memory values are fixed combinations, e.g., cpu: 256
↔ memory: 512 or 1024
.
Service: Running the Workload
Want durable, self-healing deployments? Use ECS Services. Update the task definition, and ECS handles rolling upgrades.
aws ecs create-service \
--cluster my-fargate-cluster \
--service-name my-app-service \
--task-definition my-app-task \
--desired-count 2 \
--launch-type FARGATE \
--network-configuration 'awsvpcConfiguration={subnets=["subnet-abc123"],securityGroups=["sg-123456"],assignPublicIp="ENABLED"}'
- Subnets: private for internal APIs, public with assignPublicIp="ENABLED" for external endpoints.
- Security Groups: open port 8080 (or your app port) for inbound traffic.
Side note: ECS will not auto-create subnets or security groups; ensure these exist first.
Verification
Check ECS Service status in AWS Console, or via CLI:
aws ecs list-tasks --cluster my-fargate-cluster --service-name my-app-service
Each running task gets an ENI and IP. Hit the public IP on the container port:
curl http://<task-ip>:8080/
Look for status 200/OK—real payload should appear.
If you get “Connection refused”: double-check security group ingress and port mappings.
Operational Enhancements
ALB Integration
For production, attach an Application Load Balancer (ALB) to the ECS Service.
Benefits: health checks, sticky sessions, SSL termination, path-based routing.
Example mapping:
ALB Listener | Target Group | ECS Task |
---|---|---|
HTTPS:443 | app-tg-8080 | 8080/tcp |
Enable deregistration delay for zero-downtime deploys.
ECS Console > Service > Load balancing > Add listener rule.
Autoscaling
Managed via ECS Service Auto Scaling policies.
Scale number of tasks based on:
- Average CPU utilization (e.g., >60% for 5 min)
- Memory percent
- Custom CloudWatch Alarm
CLI Example:
aws application-autoscaling register-scalable-target \
--service-namespace ecs \
--resource-id service/my-fargate-cluster/my-app-service \
--scalable-dimension ecs:service:DesiredCount \
--min-capacity 2 --max-capacity 10
Side effect: Too-aggressive scaling policies can exhaust subnet IPs—plan CIDR accordingly.
Key Learnings & Trade-offs (Mid-Flow)
- No SSH. Debugging uses ECS Exec (since late 2022), but requires additional IAM/config.
- Cold starts: Fargate spins up in ~30 seconds, but infrequent workloads may see multi-second latency.
- Decoupling build and deploy pipelines tightens control. Pin images with digests to avoid breaking production on re-tag.
Alternatives like AWS Copilot or CDK can synthesize CloudFormation for ECS, but direct CLI and JSON gives the deepest visibility.
Summary Table
Step | CLI Command (sample) | Config Artifact |
---|---|---|
Push Docker image to ECR | docker push ... | Dockerfile |
Create ECS cluster | aws ecs create-cluster ... | - |
Register Fargate task | aws ecs register-task-definition ... | task-definition.json |
Create ECS Service | aws ecs create-service ... | - |
Attach ALB, add scaling | ECS Console / CLI | (optional) |
Unobvious tip: To minimize downtime during deployments, configure your ECS Service’s deployment type as “blue/green” (CodeDeploy integration), not just “rolling update”. This is often overlooked in quick-start guides.
Future direction: Infrastructure as Code (e.g., Terraform aws_ecs_*
resources) further shrinks manual error surface.
Not covered: Custom VPC, advanced network policies, ECS Exec troubleshooting.
Deployment can be seamless, but the edge cases—enforcing least-privilege IAM, managing service limits, avoiding CIDR exhaustion—require ongoing attention.
Questions or need a skeleton Terraform/ECS config? This can be integrated with most CI/CD pipelines—Jenkins, GitHub Actions, CodePipeline.