In the ECS vs EC2 decision, EC2 gives you virtual machines and ECS is a free container orchestrator. ECS runs your containers on EC2 instances you manage, on ECS Managed Instances that AWS manages for you, or on serverless Fargate. So the real choice is how your containers run, or whether you skip containers and use EC2 directly.
ECS vs EC2 at a glance
Find the row that matches how much host work your team will take on. The rows run from the most host work to the least, and the last column shows what ends up on your bill.
| Option | Best fit | What you operate | How you pay |
|---|---|---|---|
| EC2 without ECS | Apps you won't containerize, OS or kernel control, BYOL licenses on Dedicated Hosts | Everything: OS, patching, deployments, scaling | The instance, with every EC2 purchase option |
| ECS on self-managed EC2 | Containers that need custom AMIs, bridge networking, host access, Windows instances or dense packing | Instances, AMIs, the Auto Scaling group and ECS agent; ECS places and restarts containers | The EC2 price only; ECS adds no charge |
| ECS Managed Instances | Containers that need EC2 instance choice (GPUs, sizes beyond Fargate, privileged capabilities) without patching hosts | Task definitions, IAM roles, instance attributes; AWS provisions, patches and replaces instances | The EC2 price plus a per-instance management fee, both per second with a one-minute minimum |
| ECS on Fargate | Small and spiky services, teams that want no hosts at all | Task definitions only | Per vCPU-hour and GB-hour for each task, per second with a one-minute minimum on Linux |
ECS on EC2 and plain EC2 cost the same per instance hour. The difference is who restarts, deploys and scales your app. Fargate and Managed Instances charge a premium for taking host work off your plate, and the worked example shows where that premium stops paying for itself.
Why "ECS or EC2" is the wrong question
ECS has no compute of its own, and AWS charges nothing for ECS orchestration. It's a scheduler: you describe containers in a task definition, and ECS places them on one of three capacity types and keeps them running. EC2 appears twice in this picture: once as the host under ECS on EC2 and Managed Instances, and once as the non-container path where your app runs on the instance directly.
The useful question has two parts. Should this workload run in containers at all? And if it should, how much of the host do you want to own?
The four ways to run your app
Each option hands more of the stack to AWS than the one before it. You trade control for less operational work, and with Managed Instances and Fargate you also pay a higher unit price.
Plain EC2: virtual machines you run
EC2 gives you a virtual machine with full control of the operating system, kernel parameters, drivers and installed software. That control comes with the whole job: you patch the OS, ship deployments, configure the Auto Scaling group and replace unhealthy instances. You can use every EC2 purchase model: On-Demand, Savings Plans, Reserved Instances, Spot, Dedicated Hosts, Dedicated Instances and Capacity Reservations.
Pick it when the app isn't containerized and won't be soon, when you need OS-level control a container can't give you, or when licensing ties you to Dedicated Hosts.
ECS on self-managed EC2
ECS on EC2 runs your containers on instances you register to the cluster, usually through an Auto Scaling group capacity provider. ECS handles placement, restarts, rolling deployments and load balancer registration, and you own the instances. Turn on managed scaling so ECS grows and shrinks the Auto Scaling group with task demand, and managed termination protection so scale-in doesn't terminate instances that still run tasks.
Use the ECS-optimized AMI on Amazon Linux 2023 or Bottlerocket. The original Amazon Linux AMI variant reached end of life on September 15, 2025. This is the only ECS option that supports custom AMIs and bridge networking. Because ECS adds nothing to the bill, Reserved Instances and EC2 Instance Savings Plans you already hold apply as usual.
It's the right home for containers that need custom AMIs, bridge networking, host-level agents or SSH access to the host, and for teams already holding commitments on specific instance families.
ECS Managed Instances: what AWS runs and what you own
ECS Managed Instances sits between self-managed EC2 and Fargate. It launched in September 2025 and has been available in all commercial Regions since October 2025. The instances are real EC2 instances in your account, but ECS provisions them, picks cost-optimized types by default, consolidates tasks onto fewer instances, patches them and replaces them.
You pay the EC2 instance price plus a management fee per instance (not per task), both billed per second with a one-minute minimum. At us-east-1 On-Demand rates, the fee for general-purpose instances is about 12% of the instance price. Savings Plans, Reserved Instances and Spot discount the EC2 part only; the fee stays the same whatever purchase option you use. Accelerated instances pay less: from July 1, 2026, AWS cut their fees by 35% for G-series and 60% for P-series and Trainium.
You still own the infrastructure role and instance profile, optional instance attributes (vCPU count, CPU manufacturer, accelerators or specific instance types), the EBS data volume size, EC2 quotas, and apps that tolerate instances being replaced.
You can't run Windows containers, bring a custom AMI (it's Bottlerocket only), SSH into the host (you use ECS Exec instead), or use bridge networking (awsvpc and host only). ECS also drains instances after 14 days, so standalone tasks that can't be stopped and replaced within that window don't fit.
Reach for it when you need EC2 instance choice, such as GPUs, tasks larger than Fargate allows or privileged Linux capabilities, but don't want to own AMIs and patching.
ECS on Fargate
Fargate removes the hosts. You set CPU and memory in the task definition and pay per vCPU-hour and GB-hour for each running task. Billing runs per second from image pull to task stop, with a one-minute minimum (five minutes for Windows). Each task runs in its own hardware virtualization environment, so it doesn't share a kernel, CPU, memory or network interface with other tasks. I cover task sizes, Fargate Spot and the networking setup in my ECS vs Fargate guide.
For a team that wants no host work at all, or services that sit idle for long stretches, Fargate is where I'd start.
One cluster can hold all three ECS capacity types at once. A single capacity provider strategy can't: it uses Managed Instances, or an Auto Scaling group, or Fargate with Fargate Spot. So "baseline on EC2, burst to Fargate" means two services, not one. You can move a service between capacity types later with UpdateService, which makes starting on Fargate a low-risk first step.
How ECS and EC2 differ in practice
The day-to-day differences come down to what you deploy, how traffic reaches it, and where its data lives.
Unit of deployment and scaling
On plain EC2, the instance is the unit. You scale by adding instances through an Auto Scaling group, and you ship a release by baking a new AMI or updating instances in place. Instances take minutes to boot.
On ECS, the task is the unit. Service Auto Scaling changes the task count, and rolling deployments can start new tasks before stopping old ones. Containers usually start in seconds once capacity exists. On self-managed EC2 and Managed Instances, capacity providers add instances when tasks don't fit; on Fargate, there's no capacity to plan.
Networking
With the awsvpc network mode, each task gets its own elastic network interface, private IP and security group. AWS recommends awsvpc unless you have a specific need for another mode. Fargate supports awsvpc only, Managed Instances supports awsvpc and host, and self-managed EC2 adds bridge (the default when you don't set a mode) and none. Because awsvpc uses one network interface per task, ENI limits cap how many tasks fit on an instance. The awsvpcTrunking account setting raises that density on EC2.
Storage
Amazon EBS and Amazon EFS volumes work for Linux tasks across the ECS options. Fargate gives each Linux task 20 GiB of ephemeral storage by default, configurable up to 200 GiB on platform version 1.4.0 or later, and bills only the storage beyond 20 GiB. Plain EC2 and ECS on EC2 add instance store and anything you mount on the host.
Comparison summary table
| Capability | EC2 only | ECS on EC2 | ECS Managed Instances | ECS on Fargate |
|---|---|---|---|---|
| Who manages hosts | You | You | AWS, in your account | AWS, no hosts visible |
| Scaling unit | Instance | Task | Task | Task |
| Network isolation | Security group per instance | Security group per task (awsvpc) | Security group per task (awsvpc) | Security group per task |
| Windows | Yes | Yes | No, Linux only | Yes, Windows Server 2019 and 2022 |
| GPU | Yes | Yes, including fractional GPU | Yes, including fractional GPU | Not offered; use EC2-backed capacity |
| Largest workload | Largest instance type | Instance-dependent | Instance-dependent | 32 vCPU / 244 GB per task (Linux) |
| Pricing | EC2 rate | EC2 rate | EC2 rate plus per-instance fee | Per vCPU and GB per task |
What ECS vs EC2 costs: a worked example
ECS itself costs nothing, so the cost question is about the capacity underneath it. The fair comparison is price per unit of vCPU and memory at the utilization you actually reach.
How each option is billed
Each capacity type has its own set of discounts:
- EC2 and ECS on EC2: On-Demand per instance, or discounted through Compute Savings Plans (up to 66%), EC2 Instance Savings Plans (up to 72%, tied to one instance family in a Region), Reserved Instances (up to 72%) or Spot (up to 90%). AWS recommends Savings Plans over Reserved Instances.
- Spot pricing: you pay the Spot price in effect, which adjusts gradually with long-term supply and demand, and EC2 sends a two-minute notice before it reclaims the instance.
- ECS Managed Instances: the EC2 price, discounted by any of the above, plus the per-instance management fee at full rate.
- Fargate: per vCPU-hour and GB-hour for each task. Compute Savings Plans cover Fargate at up to about 50%, and Fargate Spot runs interruption-tolerant Linux tasks at up to 70% off.
Worked example: 2 vCPU / 8 GB of capacity in us-east-1
Compare what 2 vCPU and 8 GB of capacity costs each way: as Fargate tasks, or as one m6i.large or m7g.large that several smaller tasks share. A single task can't reserve that whole instance, because the OS and ECS agent keep part of its memory, so read the EC2 rows as the price of a host your tasks pack onto. This example uses AWS On-Demand rates for Linux in us-east-1 as of November 2026 (see Fargate pricing) over a 730-hour month. Prices change and other Regions differ, so treat it as an illustration of the method.
| Option (2 vCPU / 8 GB) | Rate math | Per hour | Per month |
|---|---|---|---|
| Fargate x86 | 2 × $0.04048 + 8 × $0.004445 | $0.11652 | $85.06 |
| Fargate Arm (Graviton) | 2 × $0.03238 + 8 × $0.00356 | $0.09324 | $68.07 |
| EC2 m6i.large, plain or ECS on EC2 | On-Demand | $0.0960 | $70.08 |
| EC2 m7g.large (Graviton), plain or ECS on EC2 | On-Demand | $0.0816 | $59.57 |
| Managed Instances m6i.large | $0.0960 + $0.01152 fee | $0.10752 | $78.49 |
| Managed Instances m7g.large | $0.0816 + $0.009792 fee | $0.091392 | $66.72 |
The break-even is the instance price divided by the Fargate price for the same capacity. Your tasks must reserve more than that share of the instance before the instance is cheaper:
- EC2 m6i.large vs Fargate x86: $0.0960 ÷ $0.11652 = 82.4%
- EC2 m7g.large vs Fargate Arm: $0.0816 ÷ $0.09324 = 87.5%
- Managed Instances m6i.large vs Fargate x86: $0.10752 ÷ $0.11652 = 92.3%
- Managed Instances m7g.large vs Fargate Arm: $0.091392 ÷ $0.09324 = 98.0%
At this size, the Managed Instances fee almost erases the saving on Graviton. Because of that memory overhead, your tasks can't reach 100% of the instance either, which narrows the gap further.
Now make the load spiky. If Arm tasks reserving the same 2 vCPU and 8 GB run 8 hours a day for 30 days, Fargate bills 240 hours: 240 × $0.09324 = $22.38, against $68.07 always on. The m7g.large still costs $59.57 unless your Auto Scaling group or Managed Instances scales it in.
None of these figures include instance EBS volumes, the load balancer, NAT gateways, data transfer or CloudWatch Logs.
What moves the Fargate vs EC2 break-even
On-Demand is the least favorable case for EC2. Commitments and architecture choices shift the answer:
- Compute Savings Plans discount EC2 by up to 66% but Fargate by only up to about 50%, and EC2 Instance Savings Plans reach 72% on EC2 alone. Deeper EC2 discounts pull the break-even down in EC2's favor. Actual rates vary by family, term and payment option, so price your exact commitment.
- The Managed Instances fee doesn't shrink with a Savings Plan. As the EC2 part gets cheaper, the fee becomes a larger share of what you pay.
- Spot and Fargate Spot both suit work that tolerates interruption, such as batch jobs and queue workers.
- Graviton lowers both sides, but it raises the break-even at this size. Fargate Arm is 20% cheaper than Fargate x86, while the m7g.large is 15% cheaper than the m6i.large.
For other task sizes, instance families and Regions, run the numbers in the AWS Fargate vs EC2 Pricing Calculator.
Security: who patches what
Cost is half of the operational trade-off. The other half is security, and it follows the host split: whoever owns the host owns its patching, its credentials and its exposure.
On plain EC2, you own the whole stack: OS patches, access through SSH keys or Session Manager, the instance role and the metadata service settings. My guide to EC2 instance metadata covers enforcing IMDSv2.
On ECS on EC2, containers share the instance, so they can reach the instance metadata endpoint and inherit the instance role unless you block it. AWS strongly recommends blocking it. In awsvpc mode, set ECS_AWSVPC_BLOCK_IMDS=true in /etc/ecs/ecs.config. In bridge mode, add an iptables rule that drops traffic from docker+ interfaces to 169.254.169.254. Give each service its own task role and keep the instance role to what the ECS agent needs. Mixing up task roles and execution roles leaves you with broken tasks or excessive permissions.
On Managed Instances, AWS patches the Bottlerocket hosts within the EC2 event windows you configure. The root filesystem is immutable, SELinux is on, and there's no SSH. Tasks share an instance by default, and you can configure one task per instance when you need VM-level isolation.
On Fargate, AWS manages the host and isolates each task in its own virtualization boundary. Privileged containers aren't allowed, and you can add only a limited set of Linux capabilities.
For debugging on any ECS option, use ECS Exec to open a shell in a running container instead of SSH. It works on EC2, Managed Instances and Fargate, including Windows containers on Fargate. The container basics stay the same on every option:
- Scan images in Amazon ECR before you deploy them.
- Run containers as a non-root user with a read-only root filesystem where the app allows it.
- Inject secrets from Secrets Manager or Parameter Store at runtime instead of baking them into images.
- Use awsvpc so every task gets its own security group.
When to use each option
Work through two questions in order. Does the workload belong in a container? And if it does, does it need anything Fargate can't give it?
Choose EC2 without ECS when
- The app isn't containerized and no refactor is planned, such as a monolith you're lifting and shifting.
- You need kernel parameters, custom drivers or other OS-level configuration.
- Licensing requires Dedicated Hosts for bring-your-own-license software.
- The workload is a traditional Windows server role, such as an Active Directory domain controller.
Choose ECS on Fargate when
- You run a handful of services and nobody on the team wants to patch hosts.
- Load is spiky, or services sit idle for part of the day.
- Jobs run on a schedule. My guide on scheduled Fargate tasks with CDK shows the setup.
Choose Managed Instances or self-managed EC2 when
- You need GPUs, tasks larger than 32 vCPU and 244 GB, privileged Linux capabilities or specific instance families.
- You hold EC2 Instance Savings Plans, Reserved Instances or Capacity Reservations you want to use.
- Steady load packs instances above the break-even from the cost example.
Between those two, Managed Instances takes patching off your hands if the workload fits Linux, Bottlerocket and 14-day instance lifetimes. Anything that needs custom AMIs, bridge networking, Windows hosts or host access goes on self-managed EC2.
The steady-load branch uses the break-even from the cost example: once your tasks would fill more than about 82 to 88% of an instance, EC2-backed capacity is cheaper at On-Demand rates.
Moving from EC2 to ECS
If you've decided to containerize workloads that run on EC2 today, most of the migration risk sits in the app, not in ECS. Check five things before you write a Dockerfile:
- Logs go to stdout and stderr, not local files.
- The app exposes a health check endpoint.
- It shuts down cleanly when it receives SIGTERM.
- Configuration comes from environment variables or Secrets Manager, not hard-coded paths.
- No state lives on local disk; sessions and uploads move to a database, cache, Amazon S3 or Amazon EFS.
Then move in four phases:
- Pilot a non-critical service to build the task definition, pipeline and alarms.
- Run production on ECS alongside EC2 and shift traffic with ALB weighted target groups.
- Cut over fully, keeping the EC2 Auto Scaling group scaled down as a rollback path.
- Decommission the instances, AMIs and security groups once the new setup has proven stable.
Three mistakes are worth avoiding. Task definitions sized to match the old instances waste money and hurt packing. Credentials baked into images leak with every image pull. And treating containers like VMs, with SSH sessions and hand edits, breaks the redeploy-to-change model; use ECS Exec to inspect and a new deployment to change. For the infrastructure code, my guide to an Application Load Balanced Fargate Service with AWS CDK gives you a working starting point.
What this looked like for NetworkLessons
NetworkLessons.com is a Cisco certification learning platform run by a solo founder, and a Kubernetes experiment there was taking more time in cluster management than it saved. When I migrated NetworkLessons from Kubernetes to ECS Fargate, Fargate fit because the setup was one person, a few services and no appetite for host work.
WordPress and the exam system now run as separate services behind one shared Application Load Balancer. WordPress cron jobs became scheduled Fargate tasks instead of always-on containers, and everything is defined in AWS CDK and deployed by GitHub Actions with OIDC. Without nodes to patch or cluster upgrades to plan, more of the founder's time goes back into course content.
Frequently Asked Questions
Do I need to manage EC2 instances when using ECS?
Is Fargate the same as ECS?
What's the difference between ECS on EC2 and running Docker directly on EC2?
Can I use Windows containers with ECS?
Which is cheaper: ECS or EC2?
Is Fargate more expensive than EC2?
Should I use ECS or EC2 for a small app?
Can I use both EC2 and ECS in the same architecture?
Conclusion: choose the compute under your containers
ECS costs nothing on its own, so the ECS vs EC2 decision comes down to the compute under your containers and how much of it you want to run. Plain EC2 keeps full control and full responsibility. Fargate removes hosts, and at On-Demand rates it wins on price when your tasks would fill less than roughly 82 to 88% of a self-managed instance (92 to 98% against Managed Instances, once the fee is added), by a wider margin when load is spiky. Managed Instances buys EC2 instance choice without patching, for a fee that Savings Plans don't discount.
Before you commit, price your own task sizes in the calculator, or move one service to Fargate and compare its bill with the instances it replaced.
Next step