Crash Courses · Masters
Containers on AWS — ECS, ECR & Fargate
Containerize your app, push to ECR, and run it on Fargate without managing servers
19 min read+215 XP on completionCert: AWS Crash Course
Tap any word in the text below to start reading from there.
Containers on AWS — ECS, ECR & Fargate
Why Containers on AWS
Containers solve "works on my machine." You package your app + runtime + dependencies into a Docker image; it runs identically everywhere.
AWS offers three levels of container management:
- ECS on EC2 — you manage the host VMs, AWS manages orchestration
- ECS on Fargate — AWS manages everything, you specify CPU/memory
- EKS — Kubernetes on AWS (for complex multi-team microservices)
For most applications: start with Fargate.
ECR — Image Registry
Before ECS can run your container, the image must be in a registry. ECR is the AWS-native choice.
# Authenticate Docker to ECR
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin \
123456789.dkr.ecr.us-east-1.amazonaws.com
# Create a repository
aws ecr create-repository --repository-name my-app
# Build, tag, and push
docker build -t my-app .
docker tag my-app:latest 123456789.dkr.ecr.us-east-1.amazonaws.com/my-app:latest
docker push 123456789.dkr.ecr.us-east-1.amazonaws.com/my-app:latestECR Image Scanning checks your image for known CVEs. Enable on push:
aws ecr put-image-scanning-configuration \
--repository-name my-app \
--image-scanning-configuration scanOnPush=trueTask Definition
A task definition is the blueprint. Key fields:
{
"family": "my-app",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "512",
"memory": "1024",
"executionRoleArn": "arn:aws:iam::123456789:role/ecsTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::123456789:role/my-app-task-role",
"containerDefinitions": [
{
"name": "my-app",
"image": "123456789.dkr.ecr.us-east-1.amazonaws.com/my-app:latest",
"portMappings": [{ "containerPort": 3000, "protocol": "tcp" }],
"environment": [
{ "name": "NODE_ENV", "value": "production" }
],
"secrets": [
{ "name": "DATABASE_URL", "valueFrom": "arn:aws:ssm:us-east-1:...:parameter/db-url" }
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/my-app",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "ecs"
}
}
}
]
}Two IAM roles:
- executionRoleArn — used by ECS itself to pull the image from ECR and write logs to CloudWatch
- taskRoleArn — used by your application code to call other AWS services (S3, SQS, etc.)
Fargate Service with ALB
The complete production setup:
Internet → ALB (public subnet) → Target Group → Fargate Tasks (private subnet)
↕
RDS (DB subnet)Auto-scaling keeps the task count right-sized. Scale on CPU:
# Register scalable target
aws application-autoscaling register-scalable-target \
--service-namespace ecs \
--resource-id service/my-cluster/my-service \
--scalable-dimension ecs:service:DesiredCount \
--min-capacity 2 --max-capacity 10
# Scale when CPU > 70%
aws application-autoscaling put-scaling-policy \
--policy-name cpu-scaling \
--service-namespace ecs \
--resource-id service/my-cluster/my-service \
--scalable-dimension ecs:service:DesiredCount \
--policy-type TargetTrackingScaling \
--target-tracking-scaling-policy-configuration '{
"TargetValue": 70.0,
"PredefinedMetricSpecification": {
"PredefinedMetricType": "ECSServiceAverageCPUUtilization"
}
}'Secrets Management
Never put secrets in environment variables in plain text or in your Docker image. Use AWS Secrets Manager or Parameter Store:
# Store a secret
aws ssm put-parameter \
--name /my-app/database-url \
--value "postgres://..." \
--type SecureString
# Reference it in task definition via "secrets" array (shown above)
# ECS injects it as an environment variable at runtimeReading progress
0% read
In this module
ECR (Elastic Container Registry)
ECS (Elastic Container Service)
Fargate
Task Definition
0%