Containers: ECR, ECS & Fargate
A container image packages your app with its exact runtime and libraries, so it runs the same on your laptop, a colleague's machine and the cloud. AWS has three services that matter most for containers: Amazon ECR stores images, Amazon ECS runs and manages containers, and AWS Fargate provides the compute so you never manage servers.
In this lesson you will push an image to ECR, describe it in an ECS task definition for Fargate, run it as a service behind a load balancer, and see when to use ECS Express Mode or Kubernetes on EKS instead. You should already know what a Docker image and container are.
Amazon ECR: your private image registry
Elastic Container Registry (ECR) hosts private repositories of container images, close to where they run. It can scan images for known vulnerabilities, keep tags immutable so 1.0 always means the same image, and delete old images with lifecycle policies.
aws ecr create-repository --repository-name tiffin-api \
--image-scanning-configuration scanOnPush=true \
--image-tag-mutability IMMUTABLE \
--query repository.repositoryUri --output text111122223333.dkr.ecr.ap-south-1.amazonaws.com/tiffin-api
Docker must log in to ECR before pushing. The CLI produces a temporary password and pipes it straight into docker login, so it never appears on screen:
aws ecr get-login-password --region ap-south-1 | docker login --username AWS \
--password-stdin 111122223333.dkr.ecr.ap-south-1.amazonaws.com
docker build -t tiffin-api:1.0 .
docker tag tiffin-api:1.0 111122223333.dkr.ecr.ap-south-1.amazonaws.com/tiffin-api:1.0
docker push 111122223333.dkr.ecr.ap-south-1.amazonaws.com/tiffin-api:1.0Login Succeeded
The full image URI has the form account.dkr.ecr.region.amazonaws.com/repository:tag. If you build on an Apple Silicon Mac or a Graviton machine, the image is arm64; build for the architecture you will run on.
Amazon ECS concepts
- Cluster: a logical group for your containers in one Region.
- Task definition: a JSON blueprint for one or more containers: image, CPU, memory, ports, environment variables, IAM roles and logging. Each change creates a new numbered revision.
- Task: one running copy of a task definition.
- Service: keeps a chosen number of tasks running, replaces failed ones, registers them with a load balancer and rolls out new revisions.
- Capacity: where tasks run. Fargate is serverless: each task gets its own isolated compute. EC2 capacity runs tasks on instances you manage.
A task definition for Fargate
This task definition, taskdef.json, runs the tiffin API container on Fargate with a quarter vCPU and 512 MB of memory on Graviton:
{
"family": "tiffin-api",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "256",
"memory": "512",
"runtimePlatform": { "cpuArchitecture": "ARM64", "operatingSystemFamily": "LINUX" },
"executionRoleArn": "arn:aws:iam::111122223333:role/ecsTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::111122223333:role/tiffin-api-task-role",
"containerDefinitions": [
{
"name": "api",
"image": "111122223333.dkr.ecr.ap-south-1.amazonaws.com/tiffin-api:1.0",
"essential": true,
"portMappings": [{ "containerPort": 8080, "protocol": "tcp" }],
"environment": [{ "name": "GST_PERCENT", "value": "5" }],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/tiffin-api",
"awslogs-region": "ap-south-1",
"awslogs-stream-prefix": "api"
}
}
}
]
}Points to notice:
- Fargate requires
networkModeawsvpc: every task gets its own network interface, private IP address and security groups. cpuandmemoryare strings, and only certain combinations are allowed, from 0.25 vCPU up to 16 vCPU.- The execution role lets ECS pull the image from ECR and write logs. The task role is what your code uses to call AWS services, like a Lambda execution role. Keep them separate.
- The
awslogsdriver sends container output to CloudWatch Logs.
Register it:
aws ecs register-task-definition --cli-input-json file://taskdef.json \
--query taskDefinition.taskDefinitionArn --output textarn:aws:ecs:ap-south-1:111122223333:task-definition/tiffin-api:1
Run it as a service
Create a cluster and a service with two tasks in private subnets, registered with an Application Load Balancer's target group:
aws ecs create-cluster --cluster-name tiffin
aws ecs create-service --cluster tiffin --service-name tiffin-api \
--task-definition tiffin-api:1 --desired-count 2 --launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[subnet-0app1a000000000a,subnet-0app1b000000000b],securityGroups=[sg-0web234567890abcd],assignPublicIp=DISABLED}" \
--load-balancers "targetGroupArn=arn:aws:elasticloadbalancing:ap-south-1:111122223333:targetgroup/tiffin-api/0123456789abcdef,containerName=api,containerPort=8080" \
--query "service.{Name: serviceName, Desired: desiredCount, Status: status}"{
"Name": "tiffin-api",
"Desired": 2,
"Status": "ACTIVE"
}The two tasks run in two Availability Zones. To deploy version 1.1, push the new image, register a new task definition revision and run aws ecs update-service --cluster tiffin --service tiffin-api --task-definition tiffin-api:2; ECS replaces tasks gradually and can roll back automatically if new tasks fail health checks. Tasks in private subnets need a NAT gateway or VPC endpoints (for ECR, S3 and CloudWatch Logs) to pull images.
The shortcut: ECS Express Mode
Building the load balancer, target group, security groups and auto scaling yourself is a lot of work for a simple web service. Amazon ECS Express Mode, launched in late 2025, does it from a container image plus two IAM roles: it creates a Fargate service, an Application Load Balancer with an AWS-provided URL, canary deployments and auto scaling, while leaving every resource visible and editable in your account. It is AWS's recommended replacement for App Runner, and you can use it from the console, the CLI or infrastructure as code.
Kubernetes on Amazon EKS
Amazon Elastic Kubernetes Service (EKS) runs managed Kubernetes. Choose it when your team already uses Kubernetes tools (kubectl, Helm) or needs to run the same platform across clouds and data centres. EKS Auto Mode lets AWS manage the worker nodes, scaling and core add-ons. For a first containerised app on AWS, ECS is usually simpler to learn and operate.
Common mistakes
- Mixing up the execution role and the task role, so the app gets ECR permissions or cannot reach DynamoDB.
- Building
arm64images and running them onX86_64tasks (or the reverse), giving "exec format error". - Using the
latesttag in production. Use immutable version tags so you know exactly what is running. - Public IPs on tasks to reach ECR, instead of private subnets with NAT or VPC endpoints.
- Logging nothing. Without
logConfiguration, container output is lost.
Key points
- ECR stores private images; log in with
aws ecr get-login-passwordpiped todocker login. - ECS runs containers using clusters, task definitions (blueprints), tasks (running copies) and services (keep tasks running).
- Fargate is serverless container compute:
awsvpcnetworking, CPU and memory set per task. - The execution role pulls images and writes logs; the task role gives your code AWS permissions.
- ECS Express Mode builds a full web service from an image and replaces App Runner for new users.
- EKS (with Auto Mode) is the choice when you need Kubernetes.
Exercise
Write a Fargate task definition for a container exam-results-web that uses the image 111122223333.dkr.ecr.ap-south-1.amazonaws.com/exam-results:2.3, listens on port 3000, has 0.5 vCPU and 1 GB memory on x86_64, and logs to /ecs/exam-results.
Show solution
{
"family": "exam-results-web",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "512",
"memory": "1024",
"runtimePlatform": { "cpuArchitecture": "X86_64", "operatingSystemFamily": "LINUX" },
"executionRoleArn": "arn:aws:iam::111122223333:role/ecsTaskExecutionRole",
"containerDefinitions": [
{
"name": "web",
"image": "111122223333.dkr.ecr.ap-south-1.amazonaws.com/exam-results:2.3",
"essential": true,
"portMappings": [{ "containerPort": 3000, "protocol": "tcp" }],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/exam-results",
"awslogs-region": "ap-south-1",
"awslogs-stream-prefix": "web"
}
}
}
]
}In ECS, 1024 CPU units equal one vCPU, so 0.5 vCPU is "512", and memory is given in MiB, so 1 GB is "1024". A task role is only needed if the app itself calls AWS services.
Quick Quiz
1. Which ECS object is a JSON blueprint describing containers, CPU, memory and roles?
A task definition is the blueprint; tasks are running copies of it.
2. Which network mode does Fargate require?
With awsvpc, each task gets its own network interface and security groups.
3. Your container cannot read from DynamoDB, but the image pulls fine. Which role needs the permission?
The task role gives your application code AWS permissions.
4. What does ECS Express Mode create from a container image?
Express Mode automates the infrastructure for a web service on ECS and Fargate.
Learn AWS with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
