AWS Cheatsheet

ECS and Fargate

Use this AWS reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

Core Concepts

ConceptDescription
ClusterLogical grouping of tasks/services. No compute by itself.
Task DefinitionBlueprint for a container (image, CPU, memory, env vars, ports, volumes).
TaskA running instance of a task definition. Ephemeral.
ServiceKeeps N tasks running; integrates with ALB; handles rolling deploys.
EC2 Launch TypeYou manage the EC2 instances in the cluster.
Fargate Launch TypeServerless — AWS manages the underlying host. Pay per vCPU/GB/second.
ECS AnywhereRun tasks on your own on-prem servers.
Container InstanceAn EC2 instance running the ECS agent (EC2 launch type only).

Clusters

# Create Fargate cluster
aws ecs create-cluster \
  --cluster-name my-cluster \
  --capacity-providers FARGATE FARGATE_SPOT

# List clusters
aws ecs list-clusters
aws ecs describe-clusters --clusters my-cluster

# Delete (must drain tasks first)
aws ecs delete-cluster --cluster my-cluster

Task Definitions

# Register from JSON file
aws ecs register-task-definition --cli-input-json file://task-def.json

# List task definitions
aws ecs list-task-definitions --family-prefix my-app
aws ecs describe-task-definition --task-definition my-app:3

# Deregister (retire old revisions)
aws ecs deregister-task-definition --task-definition my-app:1

Minimal Fargate Task Definition

{
  "family": "my-app",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "256",
  "memory": "512",
  "executionRoleArn": "arn:aws:iam::123:role/ecsTaskExecutionRole",
  "taskRoleArn":      "arn:aws:iam::123:role/my-app-task-role",
  "containerDefinitions": [{
    "name": "app",
    "image": "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest",
    "portMappings": [{"containerPort": 3000, "protocol": "tcp"}],
    "environment": [
      {"name": "NODE_ENV", "value": "production"}
    ],
    "secrets": [
      {"name": "DB_PASSWORD", "valueFrom": "arn:aws:secretsmanager:us-east-1:123:secret:db-pass"}
    ],
    "logConfiguration": {
      "logDriver": "awslogs",
      "options": {
        "awslogs-group":         "/ecs/my-app",
        "awslogs-region":        "us-east-1",
        "awslogs-stream-prefix": "ecs"
      }
    },
    "healthCheck": {
      "command":  ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"],
      "interval": 30,
      "timeout":  5,
      "retries":  3
    }
  }]
}

Run a One-Off Task

aws ecs run-task \
  --cluster my-cluster \
  --task-definition my-app:3 \
  --launch-type FARGATE \
  --network-configuration '{
    "awsvpcConfiguration": {
      "subnets": ["subnet-0priv1","subnet-0priv2"],
      "securityGroups": ["sg-0abc123"],
      "assignPublicIp": "DISABLED"
    }
  }'

# Wait for task to stop
aws ecs wait tasks-stopped --cluster my-cluster --tasks arn:aws:ecs:...

# Get exit code
aws ecs describe-tasks \
  --cluster my-cluster \
  --tasks arn:aws:ecs:... \
  --query 'tasks[0].containers[0].exitCode'

Services

# Create service (Fargate, behind ALB)
aws ecs create-service \
  --cluster my-cluster \
  --service-name my-svc \
  --task-definition my-app:3 \
  --desired-count 2 \
  --launch-type FARGATE \
  --network-configuration '{
    "awsvpcConfiguration": {
      "subnets": ["subnet-0priv1","subnet-0priv2"],
      "securityGroups": ["sg-0abc123"],
      "assignPublicIp": "DISABLED"
    }
  }' \
  --load-balancers '[{
    "targetGroupArn": "arn:aws:elasticloadbalancing:...",
    "containerName": "app",
    "containerPort": 3000
  }]' \
  --deployment-configuration \
    minimumHealthyPercent=100,maximumPercent=200

# Update service (new image or desired count)
aws ecs update-service \
  --cluster my-cluster \
  --service my-svc \
  --task-definition my-app:4 \
  --desired-count 3

# Force a new deployment (re-pull :latest)
aws ecs update-service \
  --cluster my-cluster \
  --service my-svc \
  --force-new-deployment

# Wait for stable
aws ecs wait services-stable --cluster my-cluster --services my-svc

# Scale to zero
aws ecs update-service \
  --cluster my-cluster \
  --service my-svc \
  --desired-count 0

# Describe
aws ecs describe-services --cluster my-cluster --services my-svc
aws ecs list-services --cluster my-cluster

# Delete
aws ecs delete-service --cluster my-cluster --service my-svc --force

Deployment Strategies

StrategyConfigUse Case
Rolling updateminimumHealthyPercent=50, maximumPercent=200Default; zero downtime
Blue/greenCodeDeploy integrationInstant rollback, canary
ExternalAny orchestratorFull custom control
# Enable blue/green via CodeDeploy
aws ecs create-service ... \
  --deployment-controller Type=CODE_DEPLOY

Auto Scaling

# Register ECS service as scalable target
aws application-autoscaling register-scalable-target \
  --service-namespace ecs \
  --resource-id service/my-cluster/my-svc \
  --scalable-dimension ecs:service:DesiredCount \
  --min-capacity 1 \
  --max-capacity 20

# Target tracking (keep avg CPU at 60%)
aws application-autoscaling put-scaling-policy \
  --service-namespace ecs \
  --resource-id service/my-cluster/my-svc \
  --scalable-dimension ecs:service:DesiredCount \
  --policy-name cpu-target-tracking \
  --policy-type TargetTrackingScaling \
  --target-tracking-scaling-policy-configuration '{
    "TargetValue": 60.0,
    "PredefinedMetricSpecification": {
      "PredefinedMetricType": "ECSServiceAverageCPUUtilization"
    },
    "ScaleInCooldown": 60,
    "ScaleOutCooldown": 30
  }'

ECR — Container Registry

# Create repository
aws ecr create-repository --repository-name my-app

# Authenticate Docker to ECR
aws ecr get-login-password --region us-east-1 | \
  docker login --username AWS --password-stdin \
  123456789012.dkr.ecr.us-east-1.amazonaws.com

# Build, tag, push
docker build -t my-app .
docker tag my-app:latest 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest
docker push          123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest

# List images
aws ecr list-images --repository-name my-app
aws ecr describe-images --repository-name my-app \
  --query 'imageDetails[*].[imageTags[0],imagePushedAt,imageSizeInBytes]' \
  --output table

# Lifecycle policy (keep only last 10 images)
aws ecr put-lifecycle-policy \
  --repository-name my-app \
  --lifecycle-policy-text '{
    "rules":[{
      "rulePriority":1,
      "description":"Keep last 10",
      "selection":{"tagStatus":"any","countType":"imageCountMoreThan","countNumber":10},
      "action":{"type":"expire"}
    }]
  }'

# Enable image scanning on push
aws ecr put-image-scanning-configuration \
  --repository-name my-app \
  --image-scanning-configuration scanOnPush=true

aws ecr delete-repository --repository-name my-app --force

Exec into Running Task (ECS Exec)

# Enable execute-command on service
aws ecs update-service \
  --cluster my-cluster \
  --service my-svc \
  --enable-execute-command

# Shell into container
aws ecs execute-command \
  --cluster my-cluster \
  --task arn:aws:ecs:... \
  --container app \
  --interactive \
  --command "/bin/sh"

Task role needs ssmmessages:CreateControlChannel + ssmmessages:CreateDataChannel + ssmmessages:OpenControlChannel + ssmmessages:OpenDataChannel permissions.

Fargate vs EC2 Launch Type

FargateEC2
Host managementAWSYou
PricingPer task vCPU/GB/secPer EC2 instance-hour
Spot optionFARGATE_SPOT (~70% off)Spot instances
Startup time~30s coldInstance must be in cluster
Max task size16 vCPU / 120 GBEC2 instance size
GPUsNoYes (p3, g4dn)
Best forMicroservices, APIs, batchGPU workloads, max control

Fargate Spot

aws ecs create-service ... \
  --capacity-provider-strategy \
    capacityProvider=FARGATE_SPOT,weight=4 \
    capacityProvider=FARGATE,weight=1

Use FARGATE as fallback. Handle SIGTERM + drain connections within 2 minutes on interruption.

Useful Logs Commands

# Stream logs for a service
aws logs tail /ecs/my-app --follow

# Get logs for a specific task
aws logs get-log-events \
  --log-group-name /ecs/my-app \
  --log-stream-name ecs/app/<task-id>