AWS Fargate vs EC2: How Fargate Works and When to Choose It for Containers

Running Docker containers on AWS forces an early architectural decision: do you manage the underlying EC2 instances yourself, or do you let AWS handle that layer entirely? AWS Fargate exists to answer that question — it removes the server management burden so your team focuses on container workloads, not patching AMIs or right-sizing instance fleets.

TL;DR: AWS Fargate vs EC2 for Containers

DimensionAWS FargateEC2 (self-managed)
Server managementNone — AWS manages computeFull — you patch, scale, and maintain instances
Pricing modelPer vCPU/memory per secondPer instance-hour (On-Demand, Reserved, Spot)
Scaling granularityTask-levelInstance-level, then task-level
Kernel/OS accessNot availableFull access
GPU workloadsNot supportedSupported (GPU instance families)
Startup latencyHigher cold-start vs. pre-warmed EC2Lower if instances already running
Operational overheadLowHigh

How AWS Fargate Works

Fargate is a serverless compute engine for containers. It works with both Amazon ECS (Elastic Container Service) and Amazon EKS (Elastic Kubernetes Service). When you launch a task or pod on Fargate, AWS provisions isolated compute resources for that workload — you never see or manage the underlying host.

The key architectural difference from EC2-backed clusters: with EC2, you register instances into a cluster and the scheduler places tasks onto those instances. With Fargate, there is no cluster of instances to manage. You define the task's CPU and memory requirements, and Fargate allocates dedicated micro-VM infrastructure per task using AWS Firecracker virtualization technology. Each Fargate task runs in its own isolated kernel boundary.

graph TD A["Task Definition
cpu, memory, image, role"] --> B["ECS Scheduler"] B --> C["Fargate Launch Type"] C --> D["Firecracker Micro-VM
provisioned per task"] D --> E["ENI injected into VPC subnet"] E --> F["Container image pulled"] F --> G["Task running"] G --> H["Task stops → compute reclaimed"] H --> I["Billing ends"]
  1. Task Definition: You declare container image, CPU/memory allocation, networking mode, IAM role, and logging configuration.
  2. ECS Scheduler: Receives the task launch request and routes it to the Fargate launch type.
  3. Fargate Provisioning: AWS allocates a dedicated micro-VM (Firecracker-based) sized to your task spec. No shared host kernel with other customers.
  4. ENI Attachment: Each Fargate task gets its own Elastic Network Interface (ENI) injected into your VPC subnet, giving it a private IP directly in your network.
  5. Container Runtime: Your container image is pulled and executed. The task runs until completion or until stopped.
  6. Teardown: When the task stops, the underlying compute is reclaimed. You are billed only for the duration the task ran.
Think of EC2-backed ECS like renting a warehouse and filling it with shelves (instances), then placing boxes (containers) on those shelves. Fargate is like a courier service — you hand over a box with a label, and the courier handles the vehicle, route, and fuel. You only care about the box arriving.

Fargate Networking: awsvpc Mode

Fargate exclusively uses the awsvpc network mode. Every task receives its own ENI with a private IP address within your VPC. This has direct operational consequences that catch teams off guard when migrating from EC2-backed tasks using bridge networking mode.

With bridge mode on EC2, multiple containers share the host's ENI and use dynamic port mapping. Security group rules are applied at the instance level. With awsvpc on Fargate, security groups are applied directly to the task's ENI — giving you per-task firewall control, but also consuming one ENI per task. In subnets with limited IP space, this becomes a hard constraint to plan around.

graph LR subgraph EC2_Bridge["EC2 Bridge Mode"] Host["EC2 Host ENI"] --> T1["Task A
port 32768"] Host --> T2["Task B
port 32769"] SG1["Security Group
on instance"] -.-> Host end subgraph Fargate_AWSVPC["Fargate awsvpc Mode"] ENI1["ENI 1
10.0.1.5"] --> FT1["Task A
port 8080"] ENI2["ENI 2
10.0.1.6"] --> FT2["Task B
port 8080"] SG2["Security Group
on task ENI"] -.-> ENI1 SG3["Security Group
on task ENI"] -.-> ENI2 end
  1. EC2 bridge mode: Multiple tasks share the host ENI. Port mapping translates container ports to ephemeral host ports. Security groups apply at the instance level.
  2. Fargate awsvpc mode: Each task has a dedicated ENI with its own private IP. Security groups apply directly to the task. No port mapping required — container port is the accessible port.

IAM in Fargate: Two Distinct Roles

A common misconfiguration when starting with Fargate is conflating the two IAM roles involved. They serve different purposes and both are required for a correctly functioning task.

  • Task Execution Role: Used by the ECS agent to pull container images from ECR and publish logs to CloudWatch. This role is assumed by the ECS infrastructure, not your application code.
  • Task Role: Assumed by your application code running inside the container. This is the role your app uses to call AWS APIs (S3, DynamoDB, SQS, etc.).
🔽 Click to expand — Minimal Task Execution Role policy
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ecr:GetAuthorizationToken",
        "ecr:BatchCheckLayerAvailability",
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchGetImage",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "*"
    }
  ]
}

The trust policy for the Task Execution Role must allow ecs-tasks.amazonaws.com to assume it. The same principal applies to the Task Role.

🔽 Click to expand — Trust policy for both roles
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ecs-tasks.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Launching Your First Fargate Task with the AWS CLI

Before running the commands below, ensure you have a VPC, at least one subnet, a security group, an ECS cluster, and a task definition already created. The task definition must specify requiresCompatibilities: ["FARGATE"], networkMode: awsvpc, and explicit cpu and memory values at the task level.

🔽 Click to expand — Register a Fargate task definition
aws ecs register-task-definition \
  --family my-fargate-task \
  --requires-compatibilities FARGATE \
  --network-mode awsvpc \
  --cpu "256" \
  --memory "512" \
  --execution-role-arn arn:aws:iam::123456789012:role/ecsTaskExecutionRole \
  --task-role-arn arn:aws:iam::123456789012:role/myAppTaskRole \
  --container-definitions '[{
    "name": "my-container",
    "image": "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest",
    "essential": true,
    "portMappings": [{
      "containerPort": 8080,
      "protocol": "tcp"
    }],
    "logConfiguration": {
      "logDriver": "awslogs",
      "options": {
        "awslogs-group": "/ecs/my-fargate-task",
        "awslogs-region": "us-east-1",
        "awslogs-stream-prefix": "ecs"
      }
    }
  }]' \
  --region us-east-1
🔽 Click to expand — Run a Fargate task
aws ecs run-task \
  --cluster my-cluster \
  --launch-type FARGATE \
  --task-definition my-fargate-task \
  --network-configuration 'awsvpcConfiguration={subnets=["subnet-0abc123456789def0"],securityGroups=["sg-0abc123456789def0"],assignPublicIp=DISABLED}' \
  --region us-east-1

Set assignPublicIp=ENABLED only if the task is in a public subnet and needs direct internet access without a NAT Gateway. For production workloads in private subnets, use DISABLED and route outbound traffic through a NAT Gateway.

When Fargate Makes Sense — and When It Doesn't

The decision isn't purely philosophical. It comes down to observable operational trade-offs.

Choose Fargate when:

  • Your team lacks the bandwidth to manage EC2 patching, AMI updates, and cluster capacity planning.
  • Workloads are bursty or intermittent — you pay per second of task runtime, not for idle instance capacity.
  • You need strong task-level isolation for compliance or multi-tenant workloads. Each Fargate task runs in a dedicated Firecracker micro-VM.
  • You're running batch jobs or scheduled tasks where spinning up a full EC2 cluster is wasteful.

Choose EC2-backed clusters when:

  • You need GPU instances for ML inference or training workloads. Fargate does not support GPU instance types.
  • You require access to the host OS, custom kernel modules, or specific Linux capabilities not available in Fargate.
  • Your workloads run continuously at high density and EC2 Reserved Instances or Savings Plans provide meaningful cost advantages over Fargate per-second pricing.
  • You need Windows containers — while Fargate supports Windows containers on ECS, verify current regional availability and supported Windows versions in the AWS documentation before committing.

The Misdiagnosis Engineers Make with AWS Fargate Costs

A team migrates a fleet of long-running API services from EC2-backed ECS to Fargate. The first monthly bill is significantly higher than expected. The instinct is to blame Fargate's per-second pricing model.

The actual cause is almost always over-provisioned task CPU and memory. On EC2, teams tolerate over-provisioning because unused capacity on a running instance costs nothing extra per task. On Fargate, every vCPU and GB of memory you declare in the task definition is billed — whether the container uses it or not. A task declared with 2 vCPU and 4 GB running at 5% CPU utilization is paying for 95% of wasted compute.

The fix is to right-size task definitions using CloudWatch Container Insights metrics for CPU and memory utilization, then iteratively reduce allocations to match actual workload profiles. Fargate pricing and available CPU/memory combinations are defined in the AWS documentation — not all combinations are valid, and the valid pairs constrain your right-sizing options.

Over-provisioning on EC2 is a capacity buffer. Over-provisioning on Fargate is a direct line item on your bill.

Fargate with EKS: Fargate Profiles

When using Fargate with Amazon EKS, the mechanism differs from ECS. You define Fargate Profiles that specify namespace and label selectors. When a pod matches a profile's selectors, EKS schedules it onto Fargate infrastructure instead of EC2 nodes.

One behavioral detail that breaks common assumptions: DaemonSets are not supported on Fargate nodes in EKS. If your observability or security tooling relies on DaemonSets (node-level agents), those components must run on EC2 nodes. This is a documented constraint — mixed clusters with both Fargate profiles and EC2 managed node groups are a common production pattern for exactly this reason.

🔽 Click to expand — Create a Fargate profile for EKS
aws eks create-fargate-profile \
  --cluster-name my-eks-cluster \
  --fargate-profile-name my-fargate-profile \
  --pod-execution-role-arn arn:aws:iam::123456789012:role/AmazonEKSFargatePodExecutionRole \
  --subnets subnet-0abc123456789def0 subnet-0def123456789abc0 \
  --selectors '[{"namespace": "my-namespace", "labels": {"app": "my-app"}}]' \
  --region us-east-1

Wrap-Up: Choosing AWS Fargate in Production

AWS Fargate removes the EC2 fleet management layer entirely — no AMI patching, no cluster capacity planning, no instance right-sizing. The trade-off is reduced control over the host environment and a pricing model that penalizes over-provisioned task definitions. For teams running intermittent, bursty, or compliance-sensitive workloads, Fargate's operational simplicity typically outweighs the cost premium. For sustained, high-density, or GPU workloads, EC2-backed clusters remain the more cost-effective path.

Start with the AWS Fargate documentation for ECS and the EKS Fargate documentation to verify current supported configurations, regions, and CPU/memory combinations before designing your workload architecture.

Glossary

TermDefinition
Task DefinitionA blueprint in ECS that specifies container image, CPU/memory, networking, IAM roles, and logging for a containerized workload.
awsvpc network modeAn ECS networking mode that assigns each task its own ENI and private IP address within a VPC. Required for Fargate.
Task Execution RoleIAM role assumed by the ECS infrastructure to pull images from ECR and write logs to CloudWatch on behalf of a task.
Task RoleIAM role assumed by application code running inside a container to call AWS service APIs.
Fargate Profile (EKS)A configuration in EKS that maps pods matching specific namespace and label selectors to Fargate compute instead of EC2 nodes.

Related Posts

Comments

Popular posts from this blog

EC2 No Internet Access in Custom VPC: Fix Internet Gateway and Route Table

EC2 SSH Connection Timeout: Which Security Group Rules to Check

Difference Between IAM User and IAM Role: Which One Should Your EC2 Use?