JSTAcademy
0 XP
Dashboard
Crash Courses
Containers on AWS — ECS, ECR & Fargate
19 min
Masters+215 XP
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:

  1. ECS on EC2 — you manage the host VMs, AWS manages orchestration
  2. ECS on Fargate — AWS manages everything, you specify CPU/memory
  3. 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:latest

ECR 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=true

Task 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 runtime
0%