Amazon ECS
A managed orchestrator that runs your containers as tasks on either serverless Fargate or your own EC2 instances. Here's when ECS is the right call, what it costs, how it connects to the rest of your architecture, and the container mistakes Design Beaver catches as you draw.
What ECS is
ECS runs your containers and keeps them running. You describe a workload in a task definition — which image, how much CPU and memory, which ports, which IAM roles — and ECS launches it as one or more tasks. Group those into a long-running service and ECS holds the desired count steady, rescheduling any task that dies.
Where those containers actually land is the launch type: serverless Fargate, or EC2 instances in a cluster you own and patch. Same ECS either way, same task definitions and services. It sits between raw EC2, where you manage the whole server, and Lambda, where you manage nothing but a function.
Design Beaver models ECS as a container workload with its own ENI, security group, and two separate IAM roles, so it validates as you draw which edges are real and what each one needs.
When to use ECS (and when not to)
Reach for ECS when the work is containerized and runs continuously: a web or API tier behind a load balancer, background workers, queue consumers, or a handful of microservices sharing one cluster. It’s the straightforward answer when you want containers on AWS and don’t want to operate a Kubernetes control plane.
Don’t reach for it for short event-driven request/response logic — Lambda handles that without a cluster and without paying for idle tasks. For a single container with the simplest possible setup, App Runner or a plain EC2 instance is less work. If you need portable Kubernetes APIs or you already live in Helm, that’s EKS, not ECS. And purely static content belongs on S3 behind CloudFront.
Variants: where the containers run
The launch type is the one architectural choice that changes your operational surface. Everything else about ECS stays the same.
| Option | What it is |
|---|---|
| Fargate (serverless launch type)default | AWS provisions and manages the underlying compute; you specify only per-task vCPU and memory and pay per task for what it uses. No instances to patch or scale. Requires the awsvpc network mode. The default choice when you don't want to manage servers. |
| EC2 (self-managed launch type) | Tasks are placed onto EC2 instances in your cluster that you own, patch, and scale (typically via an Auto Scaling group / capacity provider). More operational work, but gives instance-level control (GPUs, specific instance types, denser bin-packing) and can be cheaper at steady high utilization. No per-task Fargate charge — you pay for the EC2 instances. |
Pick Fargate when you don’t want to manage servers at all. Pick the EC2 launch type when you need instance-level control — GPUs, a specific instance family — or when high, steady utilization makes owning the instances cheaper than paying per task.
ECS pricing in plain English
ECS itself is free. You pay for whatever compute the tasks sit on, and the launch type decides which meter runs.
There is no additional charge for ECS itself — you pay for the compute the tasks consume, and the price model depends on the launch type. Fargate: pay per task for the vCPU and memory it requests, billed per-second (1-minute minimum) from image pull start until the task stops. EC2 launch type: pay for the underlying EC2 instances and EBS you run the cluster on (standard EC2 pricing), with no per-task Fargate charge.
| Option | Representative rate |
|---|---|
| fargate | Linux/x86: ~$0.04048 per vCPU-hour + ~$0.004445 per GB-hour. Linux/ARM (Graviton): ~$0.03238 per vCPU-hour + ~$0.003560 per GB-hour. |
| ec2 | No per-task charge — billed as standard EC2 instance-hours (plus EBS) for the cluster's instances; see the EC2 entry's pricing |
Keeping the bill down
- Use Fargate Spot (or EC2 Spot capacity) for interruption-tolerant workers to cut compute cost significantly
- Use the EC2 launch type with dense bin-packing when utilization is high and steady — it can beat per-task Fargate pricing at scale
- Prefer ARM/Graviton task platforms where the image supports it — cheaper per vCPU-hour and GB-hour than x86
- Right-size the per-task vCPU/memory to actual usage rather than over-provisioning — on Fargate you pay for what the task requests
us-east-1, Linux (rates vary by region, CPU architecture, and OS). Rates as of 2026-09. Verify at the official pricing pages before using for real cost estimates — these numbers are not kept in sync with AWS pricing changes, and actual cost also depends on data transfer, load balancer, and NAT/VPC-endpoint charges.
How ECS connects to other services
Boxes and arrows hide the part that actually decides whether an ECS architecture works: which role carries which permission, and which network path the traffic takes.
The containerized application running in an ECS task connects to an RDS database as its data layer (e.g. a task running a web API talking to PostgreSQL)
ECS pulls the container image for the task from a private ECR repository when the task starts
- S3security
The application in the ECS task reads/writes S3 objects via the AWS SDK (e.g. serving user uploads, reading config/assets, writing logs or exports)
IAM:
s3:GetObjects3:PutObjects3:ListBucket - DynamoDBsecurity
The application in the ECS task reads/writes a DynamoDB table via the AWS SDK
IAM:
dynamodb:GetItemdynamodb:PutItemdynamodb:Querydynamodb:UpdateItemdynamodb:DeleteItem - SQSsecurity
The application in the ECS task sends or polls SQS messages via the AWS SDK to decouple components (e.g. a worker service draining a queue)
IAM:
sqs:SendMessagesqs:ReceiveMessagesqs:DeleteMessagesqs:GetQueueAttributes - SNSsecurity
The application in the ECS task publishes messages to an SNS topic via the AWS SDK to fan an event out to multiple subscribers
IAM:
sns:Publish An Application Load Balancer routes incoming HTTP(S) requests to the ECS service's tasks through a target group — the standard way to put a container web/app tier behind a public entry point without giving each task a public IP
API Gateway exposes the ECS service over a managed public HTTPS API using a private integration, so an internal container tier in private subnets can be reached without being made publicly routable
- Secrets Managersecurity
The containerized app reads a secret (typically the database credentials for its RDS/RDS Proxy connection, or third-party API keys) from Secrets Manager instead of baking it into the image or a plain environment variable
IAM:
secretsmanager:GetSecretValuesecretsmanager:DescribeSecret The containerized app connects to an RDS Proxy endpoint instead of directly to the DB instance, so a bursty or autoscaling task count doesn't exhaust the database's connection limit and failover is faster
The IAM point deserves saying twice, because it’s the mistake that costs the most debugging time. ECS gives a task two roles that run at different moments:
- The task execution role belongs to the ECS agent and runs before your code does, pulling the image from ECR and wiring up logging. ECR pull permissions live here.
- The task role belongs to your application code, and it’s what the AWS SDK inside the container uses to call S3, DynamoDB, SQS, or SNS. The SDK picks up its temporary credentials automatically, so there are never access keys baked into the image.
So an ECS → ECR edge is an execution-role concern, while an ECS → S3 edge is a task-role concern. Get them backwards and you get one of two confusing failures: a task that never starts because it can’t pull its image, or a running app that gets AccessDenied on its first API call.
Reaching RDS works differently again — it’s not an IAM question at all. It’s the same VPC, database credentials, and a security-group rule on the DB port. Because tasks use the awsvpc network mode, each task gets its own ENI and security group, so the RDS inbound rule references the task’s security group.
What ECS can’t connect to
Some ECS edges look reasonable on a canvas but don’t exist in AWS. Design Beaver flags them rather than letting you draw an architecture nobody can build.
An ECS service is not a Route 53 alias target and nothing resolves a domain straight to it — Route 53 points a domain at the internet-facing thing in front of the tasks (an ALB, or CloudFront), which then routes to the ECS service. There is no direct DNS-to-service edge.
Anti-patterns Design Beaver catches
These are the ECS mistakes that survive a diagram review and then show up as a surprise bill or an exposed database.
Exposing an internet-facing ECS task directly (public subnet, public IP, no load balancer) for a production web service
Why it breaksNo health-checked, single stable entry point, no even distribution across tasks, and every task IP is directly reachable from the internet — task replacements also change IPs, so clients have nothing durable to point at
Do this insteadPut an ALB (or NLB) in front, register the service with an 'ip' target group, and keep the tasks in private subnets
Putting the database (RDS) in a public subnet so the ECS tasks can reach it
Why it breaksNeedlessly exposes the database to the internet when only in-VPC connectivity is required
Do this insteadKeep RDS in a private subnet with Public access off, and allow the ECS task's security group inbound on the DB port
Running Fargate tasks in a private subnet and routing ECR image pulls out through a NAT Gateway
Why it breaksEvery image pull's layer download is billed as NAT Gateway data processing on top of the hourly NAT charge — for image traffic that never needs to leave AWS's network, that's ongoing cost for nothing
Do this insteadAdd ECR interface VPC endpoints (ecr.dkr and ecr.api) plus the S3 gateway endpoint (ECR stores layers in S3) so pulls stay on the private AWS network — cheaper and more secure than routing image traffic through a NAT Gateway
Gotchas that bite in production
- Two roles, not one. The execution role pulls the image; the task role is for your app’s AWS calls. Splitting them correctly the first time saves an afternoon.
- Target type must be
ip, notinstance. A Fargate task — anyawsvpctask — is reached at its ENI’s IP, not by an EC2 instance ID. Register the service against aniptarget group and ECS keeps task IPs added and drained automatically as tasks cycle. - API Gateway can’t target ECS directly. There is no ECS integration. Front the service with an internal ALB or NLB and connect through a VPC Link private integration.
- Private tasks can’t reach ECR by default. Add the
ecr.dkrandecr.apiinterface endpoints plus the S3 gateway endpoint. Routing pulls through a NAT Gateway works, but you pay data processing on every layer download forever. - A lone internet-facing task is not a web tier. Task replacements change IPs, so clients have nothing stable to point at and no health-checked entry point. Put a load balancer in front.
Further reading
- Official AWS documentation
- Amazon ECS on AWS Fargate
- Task execution IAM role vs task IAM role
- Why we built a validation engine
Frequently asked questions
When should you use Amazon ECS?
What is the difference between the ECS task role and the task execution role?
ecr:GetAuthorizationToken, ecr:BatchGetImage, and ecr:GetDownloadUrlForLayer go here. The task role is what your application code inside the container uses to call S3, DynamoDB, or SQS through the AWS SDK. Putting a permission on the wrong one is the most common ECS IAM mistake, and it produces two different symptoms — a task that can't pull its image, or an app that gets AccessDenied at runtime.Can API Gateway connect directly to an ECS service?
How much does Amazon ECS cost?
Do ECS tasks in a private subnet need a NAT Gateway to pull images from ECR?
ecr.dkr and ecr.api) plus the S3 gateway endpoint instead — ECR stores image layers in S3, which is why the S3 endpoint is required too.Validate your ECS architecture as you draw
Design Beaver checks your AWS design in real time — missing queues, invalid connections, and security anti-patterns, caught before you ship. It’s live in beta, free, and runs in your browser. Sign in with Google or GitHub to save your work.
Sign up for free! →Prefer email? Get new features in your inbox: