14.2
Amazon ECS: Launch Types, Roles, Load Balancing and Storage
Clusters, tasks and services, EC2 vs Fargate, the instance/execution/task roles, ALB integration and data volumes.
30 min Reading + animations
Tip: tap or hover any dotted-underlined term to see what it means.
By the end of this lesson you will be able to
- Explain what Amazon ECS does and name its building blocks: cluster, task definition, task, service and the ECS agent.
- Follow a service from a task definition to running tasks, and predict what it does when a container crashes, an instance dies or a new version is deployed (rolling, blue/green, circuit breaker).
- Choose between the EC2 and Fargate launch types: who manages what, how each is billed (Fargate per second, 1-minute minimum, for the vCPU and memory you configure) and which exam keywords point to each; place Fargate Spot, capacity providers, ECS Managed Instances and ECS Anywhere.
- Tell the three IAM roles apart (instance role, task execution role, task role), give each container least privilege, and inject secrets from Secrets Manager or Parameter Store.
- Pick a network mode (bridge, host, awsvpc), connect a service to an ALB with dynamic host ports or IP targets, and write the right security group rules.
- Give tasks the right storage: ephemeral (20 to 200 GiB on Fargate), EFS for shared persistent data, an EBS volume per task, FSx for Windows File Server, bind mounts, and the new S3 Files; and ship container logs with awslogs or FireLens.
#What ECS does
Lesson 14.1 ended with a problem: Docker on one EC2 instance is easy, but with a hundred containers on many instances, somebody has to decide where each container runs, restart the ones that crash, replace the ones on a dead server, and roll out new versions without downtime. Doing that by logging in to servers is impossible. That job is container orchestration.
Amazon Elastic Container Service (ECS) is AWS’s own orchestrator. It decides where each container runs, when it starts and stops, and keeps the number you asked for running. It is free to use: you pay only for the compute underneath (EC2 instances or Fargate) and the other resources (load balancers, volumes, logs). Its open-source counterpart, managed Kubernetes, is EKS (lesson 14.4).
#The building blocks
| Term | What it is | Analogy (the restaurant from lesson 14.1) |
|---|---|---|
| Cluster | A logical group of tasks and services, plus the capacity they run on (your EC2 instances, Fargate, or both). Regional; spans the AZs you choose. | The restaurant chain’s branch in one city |
| Task definition | A versioned JSON “recipe” for one or more containers: image, CPU, memory, ports, environment and secrets, IAM roles, log settings, volumes, network mode. Each change creates a new revision (checkout:7, checkout:8). | The recipe card, with a version number |
| Task | One running copy of a task definition: up to 10 containers that are placed together and start and stop together (an app plus a log or proxy sidecar, for example). | One dish being cooked |
| Service | Keeps a desired count of tasks running, replaces failed ones, registers them with a load balancer and handles deployments. | The kitchen manager who keeps six cooks on shift |
| ECS agent | Software on each EC2 instance of the cluster that registers the instance and starts and stops containers on ECS’s instructions. (On Fargate, AWS runs it for you.) | The shift supervisor at each counter |
The notes call a cluster “all your containers plus the servers they run on”. That is true for the EC2 launch type, but a cluster is really a logical boundary: with Fargate there are no servers of yours in it at all, and one cluster can mix Fargate tasks and EC2 instances.
#Task definitions
A task definition for the Dhaka shop’s checkout API, shortened:
{
"family": "checkout",
"requiresCompatibilities": ["FARGATE"],
"networkMode": "awsvpc",
"cpu": "512",
"memory": "1024",
"executionRoleArn": "arn:aws:iam::111122223333:role/ecsTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::111122223333:role/checkout-task-role",
"containerDefinitions": [{
"name": "checkout",
"image": "111122223333.dkr.ecr.ap-southeast-1.amazonaws.com/checkout-example:1.4.2",
"essential": true,
"portMappings": [{ "containerPort": 8080 }],
"secrets": [{ "name": "DB_PASSWORD",
"valueFrom": "arn:aws:secretsmanager:ap-southeast-1:111122223333:secret:checkout/db-AbCdEf" }],
"logConfiguration": { "logDriver": "awslogs",
"options": { "awslogs-group": "/ecs/checkout", "awslogs-region": "ap-southeast-1",
"awslogs-stream-prefix": "checkout" } }
}]
}
CPU is in CPU units (1,024 units = 1 vCPU) and memory in MiB. On Fargate the task-level CPU and memory are required and must be a valid pair, from 0.25 vCPU with 0.5 to 2 GB up to 16 vCPU with up to 120 GB. Every part of this file comes back later in the lesson: the two roles, the secret, the network mode, the log driver.
#Tasks and services
You can start a task in two ways:
- Standalone task (
aws ecs run-task): runs once and is not replaced if it fails. For batch jobs, migrations and one-off scripts; scheduled and event-driven tasks are lesson 14.3. - Service (
aws ecs create-service): keeps the desired count running for ever. For anything long-lived: web APIs, workers. A service can also be a daemon service (one task on every EC2 instance, for agents such as log collectors), instead of the usual replica service.
The service scheduler picks an instance with enough free CPU, memory and ports for each task. On EC2 you can steer it with placement strategies (spread across AZs or instances, binpack to fill instances and save money, random) and constraints (distinctInstance, or memberOf an expression such as an instance type). Fargate spreads tasks across the AZs of your subnets on its own.
From a task definition to tasks that stay running
The checkout API of a Dhaka e-commerce shop before the Eid sale. A task definition describes the container; an ECS service keeps three copies running on three EC2 instances of the cluster, replaces any that die, and rolls out new versions.
Scenario
Task definition → service → the scheduler places tasks → each agent starts its container.
Task definition checkout:7
image
checkout-example:1.4.2
cpu / memory
512 / 1024
containerPort
8080
networkMode
bridge
taskRoleArn
checkout-task-role
You register a task definition: the recipe for the container. Image, CPU and memory, port, roles, log settings. Each change creates a new revision (checkout:7, checkout:8…); a revision is never edited.
#The ECS agent
On the EC2 launch type every instance must run the ECS container agent to be part of the cluster. The ECS-optimized AMIs (Amazon Linux 2023, Amazon Linux 2, Bottlerocket, Windows Server) include Docker and the agent; user data tells the agent which cluster to join:
#!/bin/bash
echo "ECS_CLUSTER=checkout-example" >> /etc/ecs/ecs.config
The agent registers the instance using the instance role (below), reports free CPU, memory and ports, starts and stops containers on ECS’s instructions, and reports their state. It is open source, and on EC2 it is your job to keep it updated (a new ECS-optimized AMI, or aws ecs update-container-agent). If the agent disconnects, the instance stays in the cluster but gets no new tasks.
#Deployments: rolling, blue/green and the circuit breaker
When you update a service to a new task definition revision, ECS replaces the tasks according to the deployment strategy.
Rolling update (the default). Two numbers control it: minimum healthy percent (default 100%) and maximum percent (default 200%) of the desired count. With 4 desired tasks and the defaults, ECS may run up to 8 tasks during the deployment and never fewer than 4 healthy ones: it starts new tasks, waits for them to pass the load balancer health check, drains and stops old ones. If the cluster has no room for the extra copies, lower the maximum or the minimum (50% minimum replaces half at a time, with reduced capacity).
Blue/green. A full second set of tasks (green) starts next to the current one (blue); traffic moves over once green is healthy, and blue stays around for a quick switch back. This used to require AWS CodeDeploy. Since July 2025 ECS has its own built-in blue/green strategy, with lifecycle hooks to run tests before traffic shifts and a bake time before blue is removed, for services behind an ALB, an NLB or Service Connect; canary and linear traffic shifting followed in October 2025. Exam questions written earlier expect “CodeDeploy” for blue/green on ECS; both are correct today.
Deployment circuit breaker. Without it, a version that never becomes healthy can keep ECS retrying for a long time. With the circuit breaker on (and rollback), ECS counts failed task launches, marks the deployment failed and rolls back to the last good revision automatically. CloudWatch alarms can trigger the same rollback.
#Launch types: EC2 or Fargate
A task needs CPU and memory somewhere. ECS offers two places, the launch types, and the notes promise an exam question on them.
EC2 launch typeFargate launch type
- Tasks run on EC2 instances in your account that you launch (usually in an Auto Scaling group from an ECS-optimized AMI), each running the ECS agent.
- You manage the instances: OS patches, agent updates, AMI rotation, instance types, scaling the instances as well as the tasks.
- You pay for the instances, by the second, whether the tasks fill them or not. Reserved Instances, Savings Plans and Spot Instances all apply.
- Full control: root access to the host, SSH or Session Manager, GPUs, any instance type, custom daemons, host-level tuning.
- Serverless: no instances in your account to see or manage. Each task runs in its own isolated micro-VM on AWS-managed capacity.
- AWS patches and scales the infrastructure. You choose the task size (vCPU and memory) and the number of tasks.
- You pay per second, with a 1-minute minimum (5 minutes for Windows), for the vCPU and memory you configure for the task, from the moment the image starts to download until the task stops, plus ephemeral storage above 20 GB.
- Less control: no host access, no GPUs, network mode always awsvpc.
The notes say Fargate bills for “exactly the resources the container uses”. Not quite: Fargate bills for what the task requests in its definition. A task configured with 2 vCPU that idles at 5% CPU is billed for 2 vCPU. Right-sizing the task definition is how you save money on Fargate.
The notes also say “use reserved EC2 instances to reduce cost” points to the EC2 launch type. For Reserved Instances that is right: they only apply to EC2. But Compute Savings Plans (lesson 4.4) cover Fargate too (and Lambda), so “commit for one or three years to cut Fargate cost” has an answer.
EC2 launch type vs Fargate
The same task definition, two ways to give it a place to run. On the left you own the EC2 instances; on the right AWS runs each task on capacity you never see.
Scenario
You run the instances; ECS runs the containers on them.
You launch EC2 instances (usually from an Auto Scaling group) with the ECS-optimized AMI, which already has Docker and the ECS agent. User data writes ECS_CLUSTER=checkout-example to /etc/ecs/ecs.config.
#Which one costs less?
Per vCPU-hour, Fargate list prices are a bit higher than a fully used EC2 instance. But an EC2 instance is almost never fully used: tasks do not pack perfectly (memory runs out before CPU, or the other way round), instances stay up while tasks scale in, and you need spare room for deployments. Try it with the shop’s checkout service:
Fargate or EC2: what does one service cost?
The checkout API of a Dhaka e-commerce shop runs as an ECS service. Choose the task size, how many tasks, how many hours a day, and for the EC2 launch type the instance type and network mode. Then calculate a 30-day bill both ways.
Task size
Running tasks
Hours a day
EC2 instance type
Network mode on EC2
Press “Calculate” to see the result.
With the defaults (10 tasks of 0.5 vCPU and 1 GB, all day, m6i.large), Fargate costs about 178 USD for 30 days and three instances about 207 USD, because four tasks fit per instance and the third instance is only half full. With 20 tasks the instances are full and EC2 wins (346 against 355 USD). Add the hours you spend patching and scaling instances, and the usual advice follows: start on Fargate; move steady, large, well-packed workloads to EC2 with Savings Plans or Reserved Instances when the numbers say so.
A worked example to keep: one task of 1 vCPU and 2 GB, running all month (30 days = 720 hours) in N. Virginia: (1 × 0.04048 + 2 × 0.004445) × 720 = 0.04937 × 720 ≈ 35.55 USD. Ten such tasks for 8 hours a day: 0.04937 × 10 × 240 ≈ 118.49 USD.
#Fargate Spot and capacity providers
Fargate Spot runs Fargate tasks on spare AWS capacity at up to 70% off. AWS can take the capacity back with a two-minute warning (the task gets SIGTERM, then is stopped). It suits interruption-tolerant work: queue workers, batch jobs, test environments. It runs Linux tasks only.
How do you choose between FARGATE, FARGATE_SPOT and your own instances today? With capacity providers. Instead of naming a launch type, a service uses a capacity provider strategy, for example “always 2 tasks on FARGATE (the base), and the rest split 1:3 between FARGATE and FARGATE_SPOT”. On EC2 an Auto Scaling group capacity provider with managed scaling adds instances when tasks are waiting for room. A service uses either a launch type or a capacity provider strategy, not both. The scaling side (base and weight, managed scaling, Spot interruptions) is lesson 14.3.
#IAM roles for ECS: three, not two
The notes say ECS has two roles, the EC2 instance role and the task role. There are really three, and the third, the task execution role, is the one that makes Fargate work.
| Role | Who uses it | For what | Where it is set |
|---|---|---|---|
| EC2 instance role (instance profile) | The ECS agent on an EC2 container instance. EC2 launch type only. | Registering the instance with the cluster and talking to the ECS API. (On EC2 it can also pull images and send logs for all tasks of the instance, the older way.) Managed policy AmazonEC2ContainerServiceforEC2Role. | The launch template of the instances |
| Task execution role | The ECS agent or Fargate, to start the task, before your code runs | Pulling the image from private ECR; sending logs to CloudWatch Logs with awslogs; fetching secrets from Secrets Manager or SSM Parameter Store to inject them. Managed policy AmazonECSTaskExecutionRolePolicy, plus the secrets you add. Required on Fargate for any of these. | executionRoleArn in the task definition |
| Task role | Your application code inside the containers | Whatever the app calls: S3, DynamoDB, SQS, Bedrock… Each task definition gets its own, with least privilege. | taskRoleArn in the task definition |
Execution and task roles trust the service principal ecs-tasks.amazonaws.com. The application gets the task role’s temporary credentials from a credentials endpoint inside the task (169.254.170.2, found through the AWS_CONTAINER_CREDENTIALS_RELATIVE_URI variable); the AWS SDKs pick them up automatically. The execution role’s credentials are never visible to your code.
The slides put the ECR pull, CloudWatch logs and secrets under the instance profile. On the EC2 launch type that still works, because the agent can fall back to the instance role. But then every task on the instance shares the same permissions, and on Fargate there is no instance profile at all. Put them on the execution role.
The notes’ exam trick stays exactly right: “the application inside the container needs to access an S3 bucket” → ECS task role. Never give the EC2 instance role S3 permissions for that: every container on the instance would get them too. A container without a task role, whose SDK cannot find task credentials, falls back to the instance metadata service and picks up the instance role. That is why, on EC2, you also block containers from instance metadata (ECS_AWSVPC_BLOCK_IMDS=true for awsvpc tasks; for bridge mode an iptables rule or IMDSv2 with a hop limit of 1).
Three roles, three jobs: instance, task execution, task
A bKash-style payment company runs two teams’ containers on one EC2 instance: payments (task A) and reports (task B). Only payments may read the settlement bucket. Watch which role each AWS call uses.
Scenario
The EC2 instance’s own role: lets the agent join the cluster.
The instance starts with an instance profile whose role has the AWS managed policy for ECS container instances. The ECS agent uses it to register the instance with the cluster and keep talking to the ECS API.
Two more roles you will meet in the console: the service-linked role AWSServiceRoleForECS, which ECS creates by itself to manage ENIs and register targets with load balancers, and the ECS infrastructure role, which you create when ECS must manage resources for you, such as EBS volumes for tasks or ECS Managed Instances. Neither is used by your code.
#Secrets in task definitions
Never bake passwords into images or plain environment variables in the task definition (anyone who can read the task definition sees them). Reference them instead:
secretswithvalueFrom= the ARN of a Secrets Manager secret (optionally one JSON key of it) or an SSM Parameter Store parameter (SecureStringfor secrets).- The task execution role needs
secretsmanager:GetSecretValueorssm:GetParameterson exactly those ARNs (pluskms:Decryptif a customer managed KMS key encrypts them). - ECS fetches the value when the task starts and sets it as an environment variable (or, for registry credentials, uses it to log in to a private registry). A rotated secret reaches the containers only when tasks are restarted, for example by forcing a new deployment.
Secrets Manager and Parameter Store are covered in depth in lesson 21.3.
#Networking and load balancing
#Network modes
The task definition’s networkMode decides how containers get on the network.
| Mode | How it works | Where |
|---|---|---|
| awsvpc | Each task gets its own ENI with a private IP in your subnet and its own security groups. Containers in the task share it (they reach each other on localhost). | Required on Fargate; recommended on EC2 |
| bridge | Containers sit on Docker’s virtual bridge inside the instance; container ports are mapped to host ports. Static or dynamic host ports. | EC2 (default on Linux) |
| host | Containers use the instance’s network directly; the container port is the host port. Fast, but one copy per port per instance. | EC2 |
| none | No external networking. | EC2 |
#Dynamic host port mapping and the ALB
The notes’ classic case: several copies of the same container on one EC2 instance in bridge mode. If the task definition fixed the host port (say 80), the second copy could not start on that instance: the port is taken, and the scheduler treats ports as a resource like CPU and memory, so the task waits in pending. Leave hostPort empty (or 0) and Docker assigns each copy a free port from the ephemeral range. ECS registers every task in the ALB target group as instance ID + port, and the ALB spreads traffic across them, even several on the same instance. That is dynamic host port mapping, and it is why the ALB is the usual partner of ECS.
Two details the notes leave out:
- Which ports? The range comes from the kernel (
/proc/sys/net/ipv4/ip_local_port_range), which is 32768–60999 on Amazon Linux; if it is unavailable Docker uses 49153–65535. The notes’ example “32768” is the first one. - Security group. The instances’ security group must allow those ports from the ALB’s security group. The usual rule is TCP 32768–65535 with the ALB’s security group as the source. Forget it and the health checks fail: every target is unhealthy although the containers are fine.
The Classic Load Balancer does not support dynamic host ports (it needs a fixed port on every instance) and cannot target Fargate tasks; the slides say it is supported but not recommended, and today it is legacy.
#awsvpc: an ENI per task
The notes say a Fargate task gets its own private IP, so the ALB sends traffic “straight to the IP, not to a port”. The first half is right; the second is loose. With awsvpc each task has its own ENI and IP, so there are no host ports to share: the container listens on 8080 itself, and the ALB targets IP address and port (10.0.1.23:8080). The target group must use target type ip, not instance (lesson 6.3). The gain:
- Every task is a full member of the VPC: its own security group (“8080 from the ALB’s security group” per task), its own line in VPC Flow Logs.
- No port conflicts, no ephemeral range to open on instances.
awsvpc also works on the EC2 launch type, with one catch: every task consumes an ENI, and an instance supports only a few. An m6i.large takes 3 ENIs, one is the instance’s own, so only 2 awsvpc tasks fit, however much CPU is left. Turn on the awsvpcTrunking account setting and new instances get a trunk ENI with many branch ENIs: the same m6i.large then takes up to 10 tasks.
Network modes and the load balancer
The checkout API listens on container port 8080. How does the ALB reach three copies of it: on one EC2 instance in bridge mode, or as Fargate tasks with their own IP addresses?
Scenario
Host port 0: each copy gets a free ephemeral port, the ALB learns it.
In bridge mode the containers sit on Docker’s private bridge network inside the instance. To be reachable, container port 8080 must be mapped to a port on the host.
#Which load balancer?
- Application Load Balancer: the default for HTTP/HTTPS. Path- and host-based routing lets several services share one ALB (
/checkout→ checkout service,/search→ search service). One service can register with several target groups. - Network Load Balancer: TCP, UDP or TLS, extreme throughput and low latency, static IP addresses, and exposing a service to other VPCs or accounts through AWS PrivateLink.
- Gateway Load Balancer: for fleets of virtual appliances (firewalls, inspection), rarely in front of an app.
- Classic Load Balancer: legacy; no dynamic ports, no Fargate.
Inside the cluster, services can also call each other by name with ECS Service Connect (or Cloud Map service discovery), without an internal load balancer.
#Storage: data volumes
A container’s writable layer disappears with the container, and in ECS tasks are replaced all the time: every deployment, every crash, every scale-in, every Fargate retirement. Anything that must outlive a task needs a volume outside it.
| Storage | Persists after the task? | Shared between tasks? | EC2 | Fargate |
|---|---|---|---|---|
| Ephemeral task storage | No | No (shared only by the containers of one task, through bind mounts) | The instance’s disk | 20 GiB by default, up to 200 GiB (ephemeralStorage); encrypted |
| Bind mount of a host path | On that one instance only | Tasks on the same instance | Yes | No host paths |
| Docker volume (local driver or a plugin) | Depends on the driver | Depends | Yes | No |
| Amazon EFS | Yes | Yes, across AZs | Yes | Yes (platform 1.4.0+) |
| Amazon EBS volume per task (since January 2024) | Volumes of service tasks are deleted when the task stops | No: one volume per task, in the task’s AZ | Yes (Nitro, ECS-optimized AMI) | Yes (Linux) |
| FSx for Windows File Server | Yes | Yes (SMB) | Windows container instances only | No |
| S3 Files (April 2026) | Yes (it is your S3 bucket) | Yes | Yes (since September 2026) | Yes; also ECS Managed Instances |
The details that matter:
- EFS (lesson 5.4) is a regional NFS file system with mount targets in each AZ. Tasks in any AZ, on EC2 or Fargate, mount the same file system (
efsVolumeConfiguration), optionally through an access point and with IAM authorization and encryption in transit. Fargate + EFS = serverless shared storage. This is the exam answer for “persistent multi-AZ shared storage for containers”. - EBS volumes for tasks. The notes say EBS does not work on Fargate. That was true until January 2024: ECS can now create and attach one new EBS volume per task, on Fargate and EC2. The task definition marks the volume
configuredAtLaunch; the service orrun-taskcall gives size, type (gp3, io2…), IOPS and an optional snapshot to start from. You cannot attach an existing volume, the volume lives in the task’s AZ (lesson 5.1), and volumes of service tasks are deleted when the task stops. It needs the ECS infrastructure role. Good for a big, fast scratch disk per task (transcoding, ETL, a cache); not for sharing. - Ephemeral storage on Fargate (platform 1.4.0+) is 20 GiB by default and can be raised to 200 GiB; the image itself uses part of it, and you pay for the amount above 20 GiB. It is encrypted (AES-256, AWS-owned key or your KMS key).
- FSx for Windows File Server (lesson 12.2): shared SMB storage, Active Directory integration, for Windows containers on EC2.
- S3 Files is the slides’ last page. It is real and new: since April 2026, Amazon S3 Files exposes a general purpose bucket as an NFS file system (built on EFS technology), and an ECS task can mount it (
s3filesVolumeConfiguration) on Fargate and ECS Managed Instances (since launch) and, since September 2026, on the EC2 launch type too. Files written through the mount sync to the bucket, and other systems keep using the S3 API on the same data. A task role is required, and transit encryption is always on. Use it when the data already lives in S3 and the code wants file paths. The exam does not expect it yet: “shared persistent storage for Fargate” is still EFS.
Where a task keeps its data
A Dhaka newspaper’s photo service runs two Fargate tasks in two Availability Zones. Reporters upload photos; the tasks resize them. Watch what survives when a task is replaced.
Scenario
Fast and free up to 20 GiB, and gone with the task.
Every Fargate task gets ephemeral storage: 20 GiB by default, up to 200 GiB with ephemeralStorage in the task definition (you pay for the part above 20). The image layers live there too. Containers of one task can share it through a bind mount.
#Logging: awslogs and FireLens
Containers should write logs to stdout and stderr, not to files inside the container (those vanish with the task, and nothing ships them).
awslogslog driver: sends each container’s stdout/stderr to a CloudWatch Logs log group, one log stream per container (prefix/container/task-id). On Fargate it uses the task execution role. This is the default choice and the exam answer for “view container logs in CloudWatch” (CloudWatch Logs: lesson 19.2).- FireLens: a Fluent Bit (or Fluentd) sidecar container in the task, configured with the
awsfirelenslog driver, that routes logs anywhere: Data Firehose (and through it S3), OpenSearch, CloudWatch with filtering, or third-party tools. Use it to send logs to several destinations or to transform them. - To look inside a running container without SSH, use ECS Exec (
aws ecs execute-command, through Session Manager). Metrics per task and service come from Container Insights, covered with scaling in lesson 14.3.
#What goes wrong
| Symptom | Likely cause | Fix |
|---|---|---|
Tasks stay in PENDING on an EC2 cluster | No instance has enough free CPU, memory, ports or ENIs | Add instances (capacity provider with managed scaling, lesson 14.3), smaller tasks, dynamic ports, ENI trunking |
| A second copy never starts on an instance | Fixed hostPort in bridge or host mode | Dynamic port mapping (host port 0) or awsvpc |
Fargate task stops with CannotPullContainerError | No execution role or no ECR permission; or a private subnet without NAT or VPC endpoints for ECR and S3 | Execution role with AmazonECSTaskExecutionRolePolicy; endpoints or NAT (lesson 14.1) |
Task stops with ResourceInitializationError about secrets | Execution role lacks secretsmanager:GetSecretValue / ssm:GetParameters or kms:Decrypt; no network path to the service | Add the permissions on the exact ARNs; VPC endpoints |
The app gets AccessDenied from S3 | No task role, or the task role lacks the permission (the instance role is not the place) | Task role with least privilege |
| Every container on an instance can read S3 | S3 permissions on the instance role, containers reaching instance metadata | Move them to a task role; block IMDS from containers |
| All targets unhealthy behind the ALB, containers fine | Security group of the instances does not allow the ephemeral port range from the ALB; or the target type does not fit the network mode | Allow 32768–65535 from the ALB’s security group; target type ip for awsvpc |
| Healthy tasks are killed during start-up | Health checks start before the app is ready | Health check grace period on the service; a fair health check path |
| A deployment never finishes, old and new tasks loop | New revision never becomes healthy | Deployment circuit breaker with rollback; fix the revision |
| Uploaded files disappear after a deployment | Data written to ephemeral storage | EFS (shared), EBS per task (private), or S3 |
| Containers killed with exit code 137 | Out of memory: the container exceeded its memory limit | More memory in the task definition; fix the leak |
| A rotated database password is not picked up | Secrets are injected at task start | Force a new deployment after rotation, or read the secret at runtime |
| No logs in CloudWatch | No logConfiguration, or the execution role cannot write logs | awslogs driver and execution role permissions |
#ECS in system design
- Web APIs and microservices: one ECS service per microservice, each with its own task definition, task role and scaling, behind a shared ALB with path-based routing. Fargate removes the instance fleet from the operations list.
- Workers: services without a load balancer that pull from SQS (lesson 13.1), on Fargate Spot where interruptions are acceptable; scaling on the queue is lesson 14.3.
- Batch and scheduled jobs: standalone tasks started by EventBridge (lesson 14.3), billed per second.
- Isolation between teams: separate task roles, security groups per task (awsvpc), separate clusters or accounts for strong boundaries; Fargate also gives each task its own micro-VM.
- Cost: Fargate for spiky or small workloads and fast starts; EC2 (with Savings Plans or Reserved Instances, and Spot through capacity providers) for large, steady, well-packed fleets or when you need GPUs.
- Lift and shift: Elastic Beanstalk’s Docker platform (lesson 9.4) runs containers on ECS-like infrastructure with less control; plain ECS is the next step when you outgrow it.
#Try it yourself
Create a cluster, the execution role, a task definition and a Fargate service behind an existing ALB target group, then watch a deployment. The output is shortened; IDs are examples.
REGION=ap-southeast-1
# 1. An empty cluster (Fargate needs no instances)
aws ecs create-cluster --cluster-name checkout-example --region $REGION
# { "cluster": { "clusterName": "checkout-example", "status": "ACTIVE", ... } }
# 2. The task execution role: trusted by ecs-tasks.amazonaws.com, AWS managed policy
aws iam create-role --role-name ecsTaskExecutionRole \
--assume-role-policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow",
"Principal":{"Service":"ecs-tasks.amazonaws.com"},"Action":"sts:AssumeRole"}]}'
aws iam attach-role-policy --role-name ecsTaskExecutionRole \
--policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
# 3. Register the task definition shown earlier (saved as checkout-taskdef.json)
aws ecs register-task-definition --cli-input-json file://checkout-taskdef.json
# "taskDefinitionArn": "arn:aws:ecs:ap-southeast-1:111122223333:task-definition/checkout:1"
# 4. A service: 2 tasks, private subnets, ALB target group of type ip, circuit breaker on
aws ecs create-service --cluster checkout-example --service-name checkout \
--task-definition checkout:1 --desired-count 2 --launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[subnet-0a1b2c3d4e5f60001,subnet-0a1b2c3d4e5f60002],securityGroups=[sg-0123456789abcdef0],assignPublicIp=DISABLED}" \
--load-balancers "targetGroupArn=arn:aws:elasticloadbalancing:ap-southeast-1:111122223333:targetgroup/checkout-tg/0123456789abcdef,containerName=checkout,containerPort=8080" \
--health-check-grace-period-seconds 60 \
--deployment-configuration "deploymentCircuitBreaker={enable=true,rollback=true},minimumHealthyPercent=100,maximumPercent=200"
# 5. Desired vs running, and the deployment state
aws ecs describe-services --cluster checkout-example --services checkout \
--query 'services[0].{desired:desiredCount,running:runningCount,rollout:deployments[].rolloutState}'
# { "desired": 2, "running": 2, "rollout": ["COMPLETED"] }
# 6. Each task's private IP (awsvpc) and why a task stopped
aws ecs list-tasks --cluster checkout-example --service-name checkout
aws ecs describe-tasks --cluster checkout-example --tasks 3f1c9e0a1b2c4d5e8f9a0b1c2d3e4f50 \
--query 'tasks[0].{status:lastStatus,ip:attachments[0].details[?name==`privateIPv4Address`].value|[0],stopped:stoppedReason}'
# { "status": "RUNNING", "ip": "10.0.1.23", "stopped": null }
# 7. Roll out revision 2 (register it first), then follow the logs
aws ecs update-service --cluster checkout-example --service checkout --task-definition checkout:2
aws logs tail /ecs/checkout --follow
# 8. A shell inside a running container (needs enableExecuteCommand on the service)
aws ecs execute-command --cluster checkout-example --task 3f1c9e0a1b2c4d5e8f9a0b1c2d3e4f50 \
--container checkout --interactive --command "/bin/sh"
# 9. Clean up
aws ecs update-service --cluster checkout-example --service checkout --desired-count 0
aws ecs delete-service --cluster checkout-example --service checkout
aws ecs delete-cluster --cluster checkout-example
Stop one task by hand (aws ecs stop-task) and run step 5 again: running drops to 1 and comes back to 2 within a minute. That is the service doing its job.
#Summary
Key takeaways
- ECS is AWS’s own container orchestrator, free in itself. Cluster = logical group of tasks and capacity; task definition = versioned recipe (image, CPU, memory, ports, roles, secrets, logs, volumes); task = a running copy; service = keeps the desired count running, replaces failures, registers with the load balancer, deploys. On EC2 every instance runs the ECS agent.
- Deployments: rolling (minimum healthy 100%, maximum 200% by default), blue/green (CodeDeploy, and built into ECS since July 2025), and the deployment circuit breaker for automatic rollback.
- EC2 launch type: your instances, your patches and scaling, pay per instance; root access, GPUs, Reserved Instances. Fargate: serverless, pay per second (1-minute minimum) for the configured vCPU and memory, awsvpc only. Compute Savings Plans cover Fargate too. Fargate Spot: up to 70% off, two-minute warning. Modern choice: capacity providers. Side notes: ECS Managed Instances, ECS Anywhere.
- Three IAM roles: instance role (agent joins the cluster, EC2 only), task execution role (pull from ECR, push logs, fetch secrets; required on Fargate), task role (the app’s own permissions, one per task definition). App needs S3 → task role, never the instance role.
- Secrets:
secretswith Secrets Manager or Parameter Store ARNs, fetched by the execution role and injected at task start. - Networking: bridge with dynamic host ports + ALB for many copies per instance (open 32768–65535 from the ALB’s security group); awsvpc = one ENI and IP per task, own security group, target type ip, required on Fargate (ENI trunking on EC2); NLB for TCP/UDP and PrivateLink; CLB legacy.
- Storage: ephemeral (Fargate 20 GiB default, up to 200 GiB) dies with the task; EFS = shared, persistent, multi-AZ, EC2 and Fargate (the exam answer); EBS per task (since 2024) = private, AZ-bound; FSx for Windows on EC2; S3 Files (2026) on Fargate, Managed Instances and EC2.
- Logs:
awslogsto CloudWatch Logs; FireLens to route anywhere.
Answer on your own first, then read the explanation, even when you got it right.
Self-checkQuestion
Which statement correctly describes ECS task definitions, tasks and services?
Self-checkQuestion
On the EC2 launch type, what does the ECS container agent do?
Self-checkQuestion
Which two requirements point to the EC2 launch type rather than Fargate?
Choose 2
Self-checkQuestion
How does AWS bill a Linux task on Fargate?
Self-checkQuestion
In US East (N. Virginia), Fargate costs 0.04048 USD per vCPU-hour and 0.004445 USD per GB-hour. What does one task with 1 vCPU and 2 GB cost if it runs for 30 days without stopping?
Self-checkQuestion
The shop’s checkout service runs 10 Fargate tasks of 0.5 vCPU and 1 GB, but only 8 hours a day, for 30 days (same prices as before). What is the bill?
Self-checkQuestion
A company runs all its containers on Fargate and wants a discount for committing to steady usage for three years. What should it buy?
Self-checkQuestion
An application in an ECS task must write items to a DynamoDB table. Where should the DynamoDB permission go?
Self-checkQuestion
A new Fargate task cannot start: the image in a private ECR repository in the same account cannot be pulled. The subnets have a NAT gateway. What is most likely missing?
Self-checkQuestion
A task definition injects DB_PASSWORD from a Secrets Manager secret. Which identity needs permission to read that secret?
Self-checkQuestion
Two services share EC2 container instances. Only the payments service may read an S3 bucket. Which two actions follow least privilege?
Choose 2
Self-checkQuestion
Which ECS role does not exist when tasks run on Fargate?
Self-checkQuestion
You want to run four copies of the same web container on one EC2 instance behind an Application Load Balancer. What configuration allows this?
Self-checkQuestion
ECS tasks in bridge mode with dynamic ports are registered in the ALB target group, but all targets show unhealthy although the containers work. The instances’ security group allows only port 80 from the ALB. What is the fix?
Self-checkQuestion
An ECS service on Fargate must be registered with an ALB target group. Which target type must the target group use?
Self-checkQuestion
An m6i.large has 2 vCPU, 8 GiB of memory and supports 3 ENIs. Tasks need 0.5 vCPU and 1 GiB. How many tasks fit in bridge mode, and how many in awsvpc mode without ENI trunking?
Self-checkQuestion
A service’s task definition maps container port 8080 to host port 80 in bridge mode. The desired count is 4 and the cluster has 2 EC2 instances with plenty of CPU and memory. What happens?
Self-checkQuestion
Which two statements about the awsvpc network mode are correct?
Choose 2
Self-checkQuestion
A team exposes an ECS service that handles millions of TCP connections with very low latency, and partners in other VPCs must reach it through AWS PrivateLink. Which load balancer should it use?
Self-checkQuestion
Containers on Fargate in two Availability Zones must read and write the same set of files, and the files must survive task replacement. What should the architect use?
Self-checkQuestion
The notes say “Fargate supports only EFS; EBS does not work on Fargate”. What is accurate today?
Self-checkQuestion
Which two statements about EBS volumes attached to ECS tasks are correct?
Choose 2
Self-checkQuestion
What ephemeral storage does a Linux Fargate task on platform version 1.4.0 or later get?
Self-checkQuestion
Windows containers on the ECS EC2 launch type need a shared SMB file system joined to Active Directory. Which service fits?
Self-checkQuestion
During an Eid sale, a service on the EC2 launch type tries to scale from 6 to 12 tasks, but the new tasks stay pending for a long time. CPU on the instances is almost fully reserved. What is the most likely cause and fix?
Self-checkQuestion
After a release, new tasks keep crashing and the deployment never finishes. The team wants ECS to roll back to the previous revision automatically next time. What should they enable?
Self-checkQuestion
A service has a desired count of 4 with the default deployment settings (minimum healthy 100%, maximum 200%). What happens during a rolling update?
Self-checkQuestion
Which two statements about ECS logging are correct?
Choose 2
Self-checkQuestion
What is true about the S3 Files volumes shown on the slides?
Self-checkQuestion
A logistics company wants to run ECS tasks on servers in its own warehouse data centre while managing them from the AWS console. What should it use?
Self-checkQuestion
A team needs the cheapest way to run interruption-tolerant image-processing containers without managing any servers. What fits best?