Filter concepts by levelShowing all levels.

AWS · Section 15

ECS — Containerized Applications

Level
intermediate
Read
30 min
Concepts
5

Amazon ECS orchestrates containerized applications: a task definition is the blueprint, a task is one running instance, and a service keeps a desired count running long-term, replacing failures. This section covers the core vocabulary, the EC2-vs-Fargate capacity choice, the split between task and execution IAM roles, the mechanics of a rolling deployment, and when ECS is the right level of orchestration rather than Kubernetes.

What is true here

  1. Task definition = blueprint, task = one running instance, service = keeps N tasks running, cluster = the logical grouping.
  2. EC2 capacity means you manage instances; Fargate is serverless — AWS provisions compute per task automatically.
  3. Task role = app code permissions; execution role = what ECS needs before the container starts (pull image, fetch secrets).
  4. minimumHealthyPercent and maximumPercent bound how a rolling deployment replaces tasks; the circuit breaker auto-rolls-back repeated failures.
  5. ECS fits when containers are the right shape and Kubernetes's operational surface is not wanted; choose EKS when Kubernetes portability matters.

What you will be able to do

  • Explain what a cluster, service, task, task definition, and capacity provider each are and how they relate
  • Choose between EC2 and Fargate capacity based on utilization pattern and operational preference
  • Assign task role and execution role permissions to the correct side of the app-vs-ECS-startup boundary
  • Predict how minimumHealthyPercent and maximumPercent shape a rolling deployment's replace order
  • Decide when ECS is the right orchestration choice versus EKS or a simpler compute option
From a task definition to a running ECS service
scheduledontolaunchedwithkeptrunning by

Task definition

the versioned blueprint

EC2 or Fargate

the capacity choice

Task + execution roles

Service, rolling deploy

  • Task definition — the versioned blueprint
    • leads to EC2 or Fargate (scheduled onto)
  • EC2 or Fargate — the capacity choice
    • leads to Task + execution roles (launched with)
  • Task + execution roles
    • leads to Service, rolling deploy (kept running by)
  • Service, rolling deploy

ECS — Containerized Applications

The core ECS vocabulary, EC2 vs Fargate, task vs execution roles, rolling deployments, and when ECS is the right choice.

ECS Core Vocabulary

coreintermediate

A task definition is the blueprint for a container app. A task is one running instance of that blueprint. A service keeps a desired number of tasks running long-term, replacing any that die. A cluster is the logical grouping all of this runs inside. A capacity provider decides what infrastructure (EC2 or Fargate) actually runs the tasks.

Think of it as

A task definition is a recipe. A task is one cooked meal made from that recipe. A service is a standing order to always have N meals ready, replacing any that get eaten or spoiled. The cluster is the kitchen; the capacity provider decides whether the kitchen uses your own stove (EC2) or a caterer's (Fargate).

What we're doing: See how a task definition, a service, and a cluster relate in one deployment.

ecs-deploy.shbash
aws ecs register-task-definition --cli-input-json file://web-task.json
aws ecs create-service --cluster prod --service-name web \
  --task-definition web:3 --desired-count 3 --launch-type FARGATE
1
Registering a task definition creates a new revision — "web:3" means the third registered revision of the "web" family.
2
The service targets a specific cluster and pins to task definition revision 3.
3
desired-count 3 tells the service scheduler to keep exactly three healthy tasks running at all times.

Why this works: A task definition alone does nothing — it is inert until a task or service runs it. A service is what turns "run this blueprint" into "always have three of these running," including replacing any that crash.

Running a long-lived app as a bare task instead of a service

Wrong

bash
aws ecs run-task --cluster prod --task-definition web:3

Better

bash
aws ecs create-service --cluster prod --service-name web --task-definition web:3 --desired-count 3

What you see: The application stops permanently the moment the task crashes or the underlying instance is replaced — nothing restarts it.

Why: run-task starts a task once with no ongoing supervision. A service is the layer that watches desired count and relaunches replacements — without it, ECS treats the task like the batch job it was designed for.

ECS vocabulary, top to bottom

Cluster

the logical grouping everything runs inside

Service

keeps N tasks running, replaces failures

Task

one running instance of a task definition

Task definition

the versioned blueprint — image, CPU/memory, roles

  1. Cluster — the logical grouping everything runs inside
  2. Service — keeps N tasks running, replaces failures
  3. Task — one running instance of a task definition
  4. Task definition — the versioned blueprint — image, CPU/memory, roles

Remember: Task definition = blueprint. Task = one running instance. Service = keeps N tasks running, replaces failures. Cluster = the logical grouping. Capacity provider = EC2 or Fargate.

See also: ecs on ec2 vs fargate · deployment strategies

ECS on EC2 vs Fargate

coreintermediate

Both run the same task definitions on the same ECS control plane — the difference is only in the capacity layer underneath. With EC2, you choose the instance type, count, and manage the capacity. With Fargate, AWS provisions compute per task automatically and you never see or manage an instance.

Think of it as

ECS on EC2 is renting an apartment building and deciding how many units to keep available. Fargate is a hotel — you book exactly the room you need for exactly as long as you need it, and never think about the building's maintenance.

text
EC2 capacity: you manage instances, register them to the cluster
Fargate: --launch-type FARGATE, AWS provisions compute per task, no instances to see

What we're doing: See the same task definition deployed with each launch type.

launch-types.shbash
aws ecs create-service --cluster prod --task-definition web:3 \
  --desired-count 3 --launch-type EC2
aws ecs create-service --cluster prod --task-definition web:3 \
  --desired-count 3 --launch-type FARGATE
1
EC2 launch type requires container instances already registered to the "prod" cluster with enough free capacity for 3 tasks.
3
Fargate launch type needs no pre-registered instances at all — AWS provisions the compute for these 3 tasks on demand.

Why this works: The task definition (web:3) is identical in both commands — only the launch type differs, which shows the capacity choice is orthogonal to what the application actually is.

Assuming Fargate is always more expensive because it looks "managed"

Wrong

text
# "Fargate must cost more since AWS is doing more work — always use EC2 to save money."

Better

text
# Compare against real EC2 utilization: idle/undersized EC2 capacity
# often costs more in practice than Fargate's pay-per-task pricing

What you see: An EC2-backed cluster runs at 20% average utilization while still being billed for 100% of provisioned instance capacity around the clock.

Why: EC2 capacity is billed whether or not tasks are using it, so low or spiky utilization can make it more expensive in practice than Fargate's per-task billing, despite Fargate's higher per-unit rate.

Same task definitions, different capacity layer

ECS on EC2

  • +You choose instance type and count
  • +You patch and manage the instances
  • +Often cheaper at steady, high utilization

ECS on Fargate

  • AWS provisions compute per task
  • No instances to see or manage
  • Avoids paying for idle/undersized capacity
  • ECS on EC2
    • You choose instance type and count
    • You patch and manage the instances
    • Often cheaper at steady, high utilization
  • ECS on Fargate
    • AWS provisions compute per task
    • No instances to see or manage
    • Avoids paying for idle/undersized capacity

Choosing EC2 vs Fargate capacity

Choosing EC2 vs Fargate capacity
SituationChoice
Steady, high, predictable utilizationEC2 (often cheaper at scale)
Spiky or unpredictable workloadsFargate (no idle capacity to pay for)
Need GPU instances or specialized instance typesEC2
Want zero instance patching/managementFargate
Strict per-task isolation requiredFargate (dedicated kernel per task)

Together

bash
aws ecs create-service --cluster prod --task-definition web:3 --launch-type FARGATE

Remember: EC2 capacity: you manage instances, cheaper at steady high utilization. Fargate: serverless, pay-per-task, zero instance management — same task definitions and services either way.

See also: ecs core vocabulary · when eks is justified

Task Role vs Execution Role

coreintermediate

The task role is what your application code inside the container can do — call S3, DynamoDB, and so on. The execution role is what ECS itself needs before the container even starts — pull the image, fetch secrets, write logs. They are separate roles with separate permissions, and one never substitutes for the other.

Think of it as

The execution role is the caterer's own credentials to get into the building and set up the kitchen. The task role is the badge handed to the chef once inside — what the chef personally is allowed to touch. The caterer's building-access badge does not let the chef open the safe, and the chef's badge does not get the caterer through the front door.

json
{ "taskRoleArn": "...", "executionRoleArn": "..." }  // both are optional but distinct fields on a task definition

What we're doing: See both roles set on the same task definition, each doing a different job.

task-def.jsonjson
{
  "taskRoleArn": "arn:aws:iam::111122223333:role/ecsTaskRole",
  "executionRoleArn": "arn:aws:iam::111122223333:role/ecsTaskExecutionRole",
  "family": "web"
}
2
ecsTaskRole grants the running application code permission to call AWS APIs — e.g. read from an S3 bucket.
3
ecsTaskExecutionRole is used by ECS/Fargate itself before the app even starts — to pull the image and fetch secrets.

Why this works: The permissions ECS needs to launch a task and the permissions the task's own code needs once running are genuinely different actors doing different things at different times, which is why AWS models them as two separate roles.

Putting application permissions on the execution role

Wrong

json
{ "executionRoleArn": "arn:aws:iam::111122223333:role/roleWithS3AndEcrPermissions" }

Better

json
{
  "taskRoleArn": "arn:aws:iam::111122223333:role/ecsTaskRole",
  "executionRoleArn": "arn:aws:iam::111122223333:role/ecsTaskExecutionRole"
}

What you see: The application code's AWS SDK calls fail with AccessDenied even though "the role clearly has S3 permissions" — because the app never receives the execution role's credentials at all.

Why: The execution role's credentials are used by the ECS agent/Fargate control plane itself, not vended into the running container — application code only ever receives the task role's credentials, so permissions placed on the wrong role are simply invisible to the app.

The caterer's badge vs the chef's badge

Execution role

  • +Used by ECS/Fargate itself, before app code runs
  • +Pulls the image from ECR
  • +Fetches secrets, writes logs

Task role

  • Vended to the running application code
  • Calls S3, DynamoDB, other AWS APIs
  • Never used to start the task itself
  • Execution role
    • Used by ECS/Fargate itself, before app code runs
    • Pulls the image from ECR
    • Fetches secrets, writes logs
  • Task role
    • Vended to the running application code
    • Calls S3, DynamoDB, other AWS APIs
    • Never used to start the task itself

Which role grants which permission

Which role grants which permission
NeedRole
App code reads/writes an S3 bucketTask role
Pull a private image from Amazon ECRExecution role
Inject a secret from Secrets Manager into the containerExecution role
App code calls DynamoDB using the AWS SDKTask role
Write container logs to CloudWatch LogsExecution role

Together

json
{ "taskRoleArn": "arn:aws:iam::111122223333:role/ecsTaskRole", "executionRoleArn": "arn:aws:iam::111122223333:role/ecsTaskExecutionRole" }

Remember: Task role = what the app code inside the container can do (S3, DynamoDB calls). Execution role = what ECS/Fargate needs before the container starts (pull image, fetch secrets, write logs).

See also: ecs core vocabulary · iam vocabulary

ECS Rolling Deployments

standardintermediate

minimumHealthyPercent and maximumPercent, applied to the desired task count, decide how a rolling deployment replaces tasks — whether it starts new ones before stopping old ones, or has to stop first to free capacity. The deployment circuit breaker can automatically roll back a deployment whose tasks keep failing to start.

Think of it as

minimumHealthyPercent and maximumPercent are two dials bounding how much the service can shrink and grow during a deploy — like renovating a shop floor while staying open: never below X% of registers staffed, never above Y% of registers set up at once.

json
{ "deploymentConfiguration": { "minimumHealthyPercent": 100, "maximumPercent": 200 } }

What we're doing: See how minimumHealthyPercent and maximumPercent shape a rolling deployment of 4 desired tasks.

deployment-config.jsonjson
{
  "deploymentConfiguration": { "minimumHealthyPercent": 50, "maximumPercent": 100 },
  "desiredCount": 4
}
2
With 4 desired tasks, minimumHealthyPercent 50% allows the scheduler to stop 2 existing tasks before starting 2 new ones — maximumPercent 100% means it cannot start new tasks first, since that would exceed the 4-task ceiling.

Why this works: These two percentages, combined with the desired count, are what determine whether a rolling deployment replaces tasks by starting-then-stopping (safer, needs headroom) or stopping-then-starting (no headroom needed, brief capacity dip).

Remember: minimumHealthyPercent bounds how low healthy task count can drop during a deploy; maximumPercent bounds how high total task count can climb. The deployment circuit breaker auto-rolls-back a deployment whose tasks keep failing to start.

See also: ecs core vocabulary · cross zone and health checks

When to Choose ECS

standardintermediate

ECS fits when the workload is already containerized and the team wants managed orchestration without taking on Kubernetes's operational surface. It is AWS's own opinionated orchestrator, trading some of Kubernetes's portability for a simpler operational model on AWS specifically.

Think of it as

ECS is AWS's own opinionated orchestrator — deeply integrated with IAM, ALB, CloudWatch, and Fargate — trading some of Kubernetes's portability and ecosystem breadth for a simpler operational model on AWS specifically.

text
Containerized app + want managed orchestration + prefer AWS-native simplicity over Kubernetes portability → ECS

What we're doing: Decide between ECS and EKS for a straightforward containerized web service.

decision.txttext
Team has no existing Kubernetes investment, wants to deploy
a containerized API with minimal orchestration overhead on AWS.
→ ECS with Fargate: less operational surface, native IAM integration.
1
No prior Kubernetes investment removes the main reason to prefer EKS (existing manifests, team Kubernetes expertise, multi-cloud portability).
2
Minimal orchestration overhead is exactly ECS's design trade-off — it deliberately exposes less configuration surface than Kubernetes.

Why this works: The decision is rarely about raw capability — both can run the same containers — it is about which operational model and ecosystem the team is willing to invest in maintaining.

Remember: ECS: simpler, AWS-native orchestration for containers, no Kubernetes operational overhead. Choose EKS instead when Kubernetes portability or its ecosystem specifically matters.

See also: ecs on ec2 vs fargate · when eks is justified

Advertisement