The Fargate "Tax": When to Migrate Back to ECS on EC2 Capacity Providers


Stop paying the AWS Fargate tax. Learn how migrating to ECS on EC2 Capacity Providers can slash your container compute costs by 70%. Read the full guide.

#AWS
Intermediate
Published: October 15, 2026 •18 min read

AWS Fargate is the easy button for container orchestration, but at scale, that button becomes incredibly expensive to keep pressing.

We all love Fargate for a reason. You hand it a container, tell it how much memory you need, and AWS handles the rest. No instances to patch, no AMIs to rotate, and no cluster scaling logic to overthink.

The Allure and The Trap of Fargate#

But that luxury comes with what we call the "Fargate Tax." Right now, you are paying roughly 50% to 70% more per vCPU and GB of memory compared to equivalent EC2 On-Demand instances.

When you start factoring in EC2 Spot instances or compute Savings Plans, that price gap turns into a chasm. Considering container compute typically accounts for half of a standard ECS bill, Fargate's convenience is quietly draining your cloud budget.

The good news is that moving away from Fargate doesn't mean returning to the dark ages of manually tuning Auto Scaling Groups (ASGs) in the middle of the night.

If you have steady-state, predictably scaled workloads, migrating back to ECS on EC2 using Capacity Providers can slash your compute costs by up to 70%. Best of all, it maintains the exact same hands-off, serverless scaling experience you already know. Let’s look at the math, the architecture, and how to pull off the migration without waking up your on-call engineers.

Let's break down the exact costs driving this migration. To understand why EC2 eventually wins, we have to look at how AWS charges for Fargate versus how they charge for EC2, and what happens when you start stacking containers on a single host.

The Raw Numbers: 2026 Pricing#

AWS ECS isn't a single pricing model; it's two completely different cost structures wearing the same product name.

Fargate charges you strictly for what you provision at the task level, billed per second. As of 2026 in us-east-1 (for Linux/x86), the on-demand rates are:

  • vCPU: $0.04048 per hour
  • Memory: $0.004445 per GB per hour

If you run a standard, small microservice task (1 vCPU and 2 GB of memory) 24/7 for a month (730 hours), it costs roughly $35.74.

EC2, conversely, charges you for the underlying instance, regardless of how many containers you run on it. A modern, comparable EC2 instance like the c5.xlarge (4 vCPU, 8 GB memory) costs roughly $124/month on-demand.

The Packing Advantage in EC2#

If you only have one or two small services, Fargate is cheaper because paying $35 for a few Fargate tasks is better than paying $124 for a mostly-empty EC2 instance.

However, EC2 allows you to "binpack" tasks. You can run multiple containers on that single EC2 instance, sharing the host OS resources.

Let's say your architecture grows to 4 of those standard tasks (1 vCPU, 2GB memory each).

  • Fargate Cost (4 tasks): ~$143/month
  • EC2 Cost (1 c5.xlarge instance holding 4 tasks): ~$124/month

This is the crossover point. At around 3+ steady-state tasks per instance, the math flips. The premium you pay for Fargate's "zero infrastructure management" begins to cost more than the operational overhead of managing EC2.

The Multiplier Effect: Savings Plans and Spot#

The cost gap widens significantly when you introduce commitment discounts.

Both Fargate and EC2 support Compute Savings Plans, but the discounts heavily favor EC2. A 1-year commitment typically nets a ~20% discount on Fargate, but yields a 30-40% discount on EC2.

If your workloads are fault-tolerant (like background job processing), EC2 Spot instances can offer a 70-90% discount off the on-demand rate, whereas Fargate Spot usually hovers around a 70% discount. Furthermore, if you switch your architecture to ARM using Graviton instances (m6g.xlarge), you immediately shave another ~20% off your compute bill compared to x86.

At a scale of 100 services, migrating steady-state workloads to EC2 with a Savings Plan can easily save a team over $10,000 a year.

The Fear of EC2: Why Teams Avoided It and How Capacity Providers Fixed It#

Before Capacity Providers existed, running ECS on EC2 was a masterclass in frustration. The problem wasn't the containers; the problem was that ECS and EC2 Auto Scaling Groups (ASGs) didn't talk to each other.

The Old Way: The Two-Brain Problem#

Historically, your infrastructure had two separate "brains" trying to scale your application, and they were entirely unaware of each other's actions.

  1. The ECS Brain (Service Auto Scaling): You tell ECS to scale up from 2 tasks to 10 tasks because CPU utilization spiked. ECS says, "Okay, I need to place 8 more tasks." It looks at the available EC2 instances, realizes there isn't enough CPU or memory left, and throws a CapacityUnavailable error. The tasks sit in a pending state.
  2. The EC2 Brain (ASG Scaling): You set up a separate CloudWatch alarm monitoring the entire cluster's CPU reservation. When it hits 80%, the ASG provisions a new EC2 instance.

The problem is the lag. By the time the ASG realizes the cluster is out of space, boots a new instance, runs the boot scripts, and registers it to the ECS cluster, 3 to 5 minutes have passed. Your application is already dropping requests.

The Scale-In Nightmare
Scaling down was even worse. If the ASG decided it was time to save money and kill an instance, it used its own Default Termination Policy. It didn't care if that instance was running your most critical production container or if it was completely empty. It just terminated the instance, abruptly killing the tasks on it. To prevent this, engineers had to write complex Lambda functions tying into ASG Lifecycle Hooks just to safely drain tasks before termination.

The Fix: ECS Capacity Providers#

In late 2019, AWS introduced ECS Capacity Providers, which finally forced the two brains to work together.

Instead of configuring ASG scaling policies manually, you simply tell the ECS Service to use a Capacity Provider linked to your ASG. Now, ECS is in charge of the ASG.

When ECS needs to place a task and there isn't enough room, it automatically calculates exactly how many new EC2 instances are needed and updates the ASG's desired capacity. The ASG provisions the instances, and ECS places the tasks.

Managed Scaling & Target Capacity#

The magic happens through Managed Scaling. ECS automatically creates and manages the Target Tracking scaling policies for the ASG. You just set a TargetCapacity percentage.

  • Target Capacity = 100%: ECS only provisions EC2 instances when a task absolutely needs a home. This is the cheapest option, but scaling is slow because tasks must wait for instances to boot.
  • Target Capacity = 80% (Recommended): ECS ensures that your EC2 instances are only 80% full. This leaves a 20% buffer of empty compute space in the cluster. If a traffic spike hits, ECS can immediately place new tasks into that empty buffer without waiting for new EC2 instances to boot.

Managed Termination Protection (The Lifesaver)#

The greatest benefit of Capacity Providers is that they fix the scale-in nightmare.

When you enable Managed Termination Protection, ECS actively protects instances that are running tasks. When the cluster scales down, the Capacity Provider instructs the ASG to only terminate instances that are 100% empty. Your active tasks are never abruptly killed by a rogue ASG scale-in event.

Note for the Real World: For this to work, you must enable "Instance scale-in protection" on the ASG itself, and then enable Managed Termination Protection on the Capacity Provider.

Deep Dive: Configuring EC2 Capacity Providers#

Now that we know why we want Capacity Providers, let's look at how to actually configure them. Getting this right requires navigating a few specific AWS quirks that frequently trip up both engineers in production and candidates on the AWS exams.

The configuration happens in three distinct phases: the ASG, the Capacity Provider, and the ECS Cluster association.

Step 1: Prepare the Auto Scaling Group (ASG)#

You still need an ASG, but you are going to strip it of its autonomy.
When creating the ASG:

  1. Do not attach any scaling policies. Delete your old CPU/Memory CloudWatch alarms. ECS will handle the math now.
  2. Enable Instance Scale-In Protection. This is arguably the most important checkbox. In CloudFormation, this is NewInstancesProtectedFromScaleIn: true. Without this, the ASG retains the right to kill instances based on its default termination policy (like AZ balancing or oldest launch template), regardless of what tasks are running on them.

Gotcha Alert for the ASG: If you have existing instances in the ASG when you enable this, you must manually go into the EC2 console and enable scale-in protection on the existing instances. The checkbox only applies to newly launched instances.

Step 2: Create the Capacity Provider#

Next, you create the Capacity Provider entity and link it to the ARN of the ASG you just built. Here, you define two critical settings:

1. Managed Termination Protection#

You must explicitly set ManagedTerminationProtection to ENABLED.
How it works together: Step 1 told the ASG, "Don't kill instances." Step 2 tells the Capacity Provider, "You have permission to override the ASG and kill instances only if you verify they are completely empty of ECS tasks."

2. Managed Scaling & The TargetCapacity Formula#

You enable ManagedScaling and set a TargetCapacity value between 1 and 100.

To understand TargetCapacity, you have to understand the metric AWS uses: CapacityProviderReservation.
ECS uses this formula to determine the reservation metric:
CapacityProviderReservation = (Tasks Needed / Tasks That Can Fit on Current Instances) * 100

If you set your TargetCapacity to 100%, ECS will add instances only when Tasks Needed exceeds what can fit. It scales exactly to what you need, with no buffer.

If you set your TargetCapacity to 80%, ECS will over-provision EC2 instances so that your tasks only consume 80% of the available space, leaving a 20% "warm pool" of headroom for sudden traffic spikes.

Step 3: The Cluster Association#

Finally, you associate the Capacity Provider with your ECS Cluster and define the Default Strategy.

CloudFormation
"DefaultCapacityProviderStrategy": [
  {
    "CapacityProvider": "My-EC2-Provider",
    "Base": 1,
    "Weight": 1
  }
]

The Base and Weight parameters dictate how tasks are distributed. A Base of 1 means the very first task launched in this cluster goes to this provider.

The Exam Trap: The "Unbalanced Fleet"#

Here is a classic scenario you will see on the AWS Solutions Architect exam (and in production):
You have an ASG spread across two Availability Zones (AZ-A and AZ-B). You notice that after a period of low traffic, AZ-A has 5 instances while AZ-B only has 2. The ASG is no longer balanced, which violates best practices.

  • The Trap: You might assume the ASG scaling policy is broken.
  • The Reality: This is Managed Termination Protection working as designed. If the instances in the "over-populated" AZ (AZ-A) are all running long-lived tasks, the Capacity Provider protects them. When the cluster needs to scale down, it is forced to terminate the empty instances in AZ-B, leading to an unbalanced fleet.

The Hybrid Architecture Pattern with Fargate and EC2#

If migrating entirely to EC2 feels too risky, you don't have to choose just one. One of the most powerful and underutilized features of ECS is the ability to attach multiple Capacity Providers to a single ECS Service.

This creates a Hybrid Architecture: you use cheap, reserved EC2 instances for your predictable baseline traffic, and flexible, serverless Fargate tasks to absorb sudden, unpredictable spikes.

How to Mix EC2 and Fargate#

You achieve this by creating a Capacity Provider Strategy at the ECS Service level. A strategy uses two parameters, Base and Weight to distribute tasks across your providers.

Let's assume you have an e-commerce application. You know from historical metrics that even in the middle of the night, you always need at least 10 tasks running to handle background jobs and baseline traffic. However, during a flash sale, that number might spike to 50 tasks.

You would configure your Capacity Provider Strategy like this:

  1. Provider 1: The EC2 ASG

    • Base = 10
    • Weight = 1
  2. Provider 2: Fargate (or Fargate Spot)

    • Base = 0
    • Weight = 3

Decoding the Logic#

  • The Base: The Base value dictates where the first tasks go. In this scenario, ECS will place the first 10 tasks exclusively on the EC2 provider. Since you know you always need 10 tasks, you can cover these underlying EC2 instances with a 1-year or 3-year Compute Savings Plan, securing a massive ~40% discount.
  • The Weight: Once the Base is satisfied (you have 10 tasks running on EC2), the Weight ratio kicks in for any additional tasks. The ratio here is 1:3. This means for every 4 new tasks that scale out during a flash sale, 1 goes to EC2 and 3 go to Fargate.

Why this specific ratio?#

When a flash sale hits, you need tasks online immediately. EC2 takes 3-5 minutes to boot new instances if your ASG is full. Fargate, however, can spin up tasks in seconds because AWS manages the underlying warm pool.

By routing the majority of the burst traffic (Weight = 3) to Fargate, your application scales instantly to meet the demand. By routing a small portion (Weight = 1) to EC2, you slowly begin provisioning new EC2 instances in the background. If the high traffic sustains itself over several hours, you are still capturing some cost savings on EC2, but you haven't sacrificed application responsiveness in the short term.

Exam Gotchas & Real-World Caveats#

If you are setting this up for a real production environment or if you are sitting for the AWS Solutions Architect Professional (SAP-C02) exam, understanding the happy path isn't enough. You need to know how the Capacity Provider abstraction breaks down when edge cases arise.

Here are the three most common gotchas that catch engineers off guard.

1. The Instance Warmup Period Trap (Over-provisioning)#

When ECS realizes it needs more EC2 instances, it updates the ASG's Desired Capacity. The ASG launches the instances, but instances take time to boot the OS, run User Data scripts, start the Docker daemon, and register the ECS Agent.

  • The Gotcha: During this boot time, the new instances aren't contributing to the cluster's available capacity. If ECS evaluates the CapacityProviderReservation metric again before the instances finish booting, it will think, "I'm still out of space!" and trigger another scale-out event. This leads to massive over-provisioning (e.g., launching 10 instances when you only needed 2).
  • The Fix: You must configure the instanceWarmupPeriod in the Capacity Provider configuration. This setting tells ECS exactly how long to wait (in seconds) after an instance is launched before it expects to see that instance's capacity reflected in the metrics. If your EC2 boot process takes 3 minutes, set this to at least 180 seconds.

2. Spot Instance Interruption Draining#

If you are using EC2 Spot instances in your ASG to save money, you know AWS can reclaim them with only a 2-minute warning.

  • The Gotcha: By default, the ASG handles the Spot termination by simply killing the instance. If you have tasks running on it, they are violently terminated mid-request. While Managed Termination Protection works for normal scale-in events, it does not stop AWS from reclaiming a Spot instance.
  • The Fix: You must enable Spot instance draining on the ECS Agent itself. When creating your EC2 Launch Template, add this line to your User Data script: echo ECS_ENABLE_SPOT_INSTANCE_DRAINING=true >> /etc/ecs/ecs.config
  • How it works: When the EC2 metadata service receives the 2-minute Spot Interruption notice, the ECS Agent intercepts it, instantly places the instance in a DRAINING state, stops accepting new tasks, and gracefully signals existing tasks to shut down and relocate before the instance is killed.

3. The Hidden "Tax" of EC2: AMI Maintenance#

When you use Fargate, AWS handles patching the underlying hypervisor and OS. When you use Capacity Providers, you own the EC2 instances.

  • The Gotcha: You cannot simply deploy an ECS cluster on EC2 and forget about it. The Amazon ECS-Optimized AMIs receive regular security updates. If you leave your Launch Template pointing to an old AMI, you will fail security compliance audits.
  • The Fix: You need an automated pipeline to rotate instances. The modern best practice is to use AWS Systems Manager (SSM) Parameter Store. AWS publishes the latest ECS-Optimized AMI ID to a public SSM parameter. Your Infrastructure as Code (Terraform/CloudFormation) should reference that SSM parameter directly, rather than a hardcoded ami- string.
  • The Real-World Process: When a new AMI is released, you update the Launch Template. Then, you trigger an Instance Refresh on the ASG. The ASG will slowly terminate old instances (respecting ECS Managed Termination Protection, which ensures tasks are drained first) and replace them with instances running the new AMI.

Conclusion: Stop Paying for Fargate's Simplicity When You Need Scale#

Fargate is brilliant. It abstracts away the operating system and lets you focus entirely on your containers. But that abstraction carries a heavy premiumoften 50% to 70% higher per vCPU/GB than On-Demand EC2, and exponentially more if you factor in Savings Plans or Spot instances.

For a long time, the Fargate tax was worth paying simply to avoid the nightmare of managing Auto Scaling Groups (ASGs). Engineers happily paid AWS to make the "Two-Brain Problem" go away.

But ECS Capacity Providers fundamentally changed the math. By allowing ECS to take complete control of the underlying ASG, automatically provisioning instances just-in-time and protecting them from premature termination, Capacity Providers give you the "serverless feel" of Fargate at the raw compute price of EC2.

If you are running a single microservice with 2 tasks, stick with Fargate. But if you are managing a cluster with dozens of services, a predictable baseline load, and a CFO who is asking questions about the AWS bill, it's time to bring your workloads back down to EC2. Configure your TargetCapacity, set your Base and Weight ratios to utilize a hybrid Fargate/EC2 model for burst traffic, and use Systems Manager to automate your AMI rotation.

The infrastructure savings will pay for your team's next engineering offsite.

Frequently Asked Questions#

What is the AWS Fargate "Tax"?
The "Fargate Tax" refers to the premium cost—often 50% to 70% higher per vCPU and GB of memory—that AWS charges for the convenience of serverless container orchestration. While Fargate eliminates EC2 instance patching and cluster scaling management, this premium becomes a significant financial drain for large, steady-state container workloads that run 24/7.

When is ECS on EC2 cheaper than AWS Fargate?
ECS on EC2 is cheaper than Fargate when you have a predictable, steady-state workload that allows you to efficiently pack multiple tasks onto a single host (bin-packing). The cost-saving crossover point typically occurs when you consistently run 3 or more tasks per instance. Additionally, 1-year and 3-year Compute Savings Plans offer deeper percentage discounts on EC2 instances than they do on Fargate.

How do ECS Capacity Providers work?
An ECS Capacity Provider acts as a management bridge between your ECS cluster and an EC2 Auto Scaling Group (ASG). Instead of writing custom scaling scripts, you tell the Capacity Provider your target utilization rate. When ECS needs to place a container but lacks compute space, the Capacity Provider automatically calculates the required infrastructure and instructs the ASG to launch the exact number of EC2 instances needed.

What is the CapacityProviderReservation metric?
CapacityProviderReservation is the specific CloudWatch metric ECS uses to trigger EC2 instance scaling. It is calculated using the formula: (Tasks Needed / Tasks That Can Fit on Current Instances) * 100. If this calculation exceeds the TargetCapacity percentage you defined in your provider settings, ECS triggers a scale-out event on the underlying ASG.

How does Managed Termination Protection prevent downtime?
Managed Termination Protection overrides the default scale-in behavior of an EC2 Auto Scaling Group. During a scale-in event, it intercepts the termination command and ensures the ASG only destroys EC2 instances that are 100% empty of active ECS tasks. This guarantees that your application's active requests are never abruptly severed by an infrastructure scale-down.

Can you mix Fargate and EC2 in the same ECS Service?
Yes. By utilizing a Capacity Provider Strategy, you can attach both EC2 and Fargate capacity providers to a single ECS Service. By adjusting the Base and Weight parameters, you can route your predictable, 24/7 baseline traffic to heavily discounted EC2 instances, while configuring your service to dynamically burst into serverless Fargate tasks during sudden traffic spikes.

Indika Kodagoda

Indika Kodagoda

Indika Kodagoda is a Lead DevOps Engineer, AWS certification instructor, and the creator of CloudQubes. He specializes in cloud infrastructure, automation, and modern Ruby on Rails development. When he’s not deploying code or mentoring aspiring engineers, he’s usually enjoying nature and cycling local gravel paths.


Large View