Course outline

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

TermWhat it isAnalogy (the restaurant from lesson 14.1)
ClusterA 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 definitionA 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
TaskOne 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
ServiceKeeps 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 agentSoftware 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.

ECS cluster
Instance A
Instance B
Instance C
Task definitioncheckout:7
ECS servicedesired 3
Image registry
Task 1
Task 2
Task 3

Task definition checkout:7

image

checkout-example:1.4.2

cpu / memory

512 / 1024

containerPort

8080

networkMode

bridge

taskRoleArn

checkout-task-role

Step 1 of 5
1

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.

EC2 launch type: your instances
Fargate: no instances to see
Instance 1ECS agentbooting
Instance 2ECS agentbooting
Step 1 of 4
1

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.

RoleWho uses itFor whatWhere 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 roleThe ECS agent or Fargate, to start the task, before your code runsPulling 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 roleYour application code inside the containersWhatever 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.

EC2 instance in the cluster
ECS API
CloudWatch Logs
ECS agentinstance roleinstance role
ECR
Secrets Manager
register
Step 1 of 3
1

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:

  • secrets with valueFrom = the ARN of a Secrets Manager secret (optionally one JSON key of it) or an SSM Parameter Store parameter (SecureString for secrets).
  • The task execution role needs secretsmanager:GetSecretValue or ssm:GetParameters on exactly those ARNs (plus kms:Decrypt if 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.

ModeHow it worksWhere
awsvpcEach 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
bridgeContainers 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)
hostContainers use the instance’s network directly; the container port is the host port. Fast, but one copy per port per instance.EC2
noneNo 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.

EC2 instance
Shoppers
ALBtarget group
Task 18080
Task 28080
Task 38080
Step 1 of 4
1

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.

StoragePersists after the task?Shared between tasks?EC2Fargate
Ephemeral task storageNoNo (shared only by the containers of one task, through bind mounts)The instance’s disk20 GiB by default, up to 200 GiB (ephemeralStorage); encrypted
Bind mount of a host pathOn that one instance onlyTasks on the same instanceYesNo host paths
Docker volume (local driver or a plugin)Depends on the driverDependsYesNo
Amazon EFSYesYes, across AZsYesYes (platform 1.4.0+)
Amazon EBS volume per task (since January 2024)Volumes of service tasks are deleted when the task stopsNo: one volume per task, in the task’s AZYes (Nitro, ECS-optimized AMI)Yes (Linux)
FSx for Windows File ServerYesYes (SMB)Windows container instances onlyNo
S3 Files (April 2026)Yes (it is your S3 bucket)YesYes (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 or run-task call 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.

AZ a
AZ b
Task 1photo-svc20 GiB scratch
Task 2photo-svc20 GiB scratch
Step 1 of 3
1

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).

  • awslogs log 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 awsfirelens log 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

SymptomLikely causeFix
Tasks stay in PENDING on an EC2 clusterNo instance has enough free CPU, memory, ports or ENIsAdd instances (capacity provider with managed scaling, lesson 14.3), smaller tasks, dynamic ports, ENI trunking
A second copy never starts on an instanceFixed hostPort in bridge or host modeDynamic port mapping (host port 0) or awsvpc
Fargate task stops with CannotPullContainerErrorNo execution role or no ECR permission; or a private subnet without NAT or VPC endpoints for ECR and S3Execution role with AmazonECSTaskExecutionRolePolicy; endpoints or NAT (lesson 14.1)
Task stops with ResourceInitializationError about secretsExecution role lacks secretsmanager:GetSecretValue / ssm:GetParameters or kms:Decrypt; no network path to the serviceAdd the permissions on the exact ARNs; VPC endpoints
The app gets AccessDenied from S3No 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 S3S3 permissions on the instance role, containers reaching instance metadataMove them to a task role; block IMDS from containers
All targets unhealthy behind the ALB, containers fineSecurity group of the instances does not allow the ephemeral port range from the ALB; or the target type does not fit the network modeAllow 32768–65535 from the ALB’s security group; target type ip for awsvpc
Healthy tasks are killed during start-upHealth checks start before the app is readyHealth check grace period on the service; a fair health check path
A deployment never finishes, old and new tasks loopNew revision never becomes healthyDeployment circuit breaker with rollback; fix the revision
Uploaded files disappear after a deploymentData written to ephemeral storageEFS (shared), EBS per task (private), or S3
Containers killed with exit code 137Out of memory: the container exceeded its memory limitMore memory in the task definition; fix the leak
A rotated database password is not picked upSecrets are injected at task startForce a new deployment after rotation, or read the secret at runtime
No logs in CloudWatchNo logConfiguration, or the execution role cannot write logsawslogs 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: secrets with 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: awslogs to CloudWatch Logs; FireLens to route anywhere.