Home β€Ί AWS Resources β€Ί Amazon ECS and Fargate

Amazon ECS and Fargate

Running containers on AWS: ECS vs EKS, Fargate vs EC2 launch type, scaling, service discovery, and when to use containers vs Lambda.

ECS vs EKS: Which Container Orchestrator?

Both run containers. The difference is complexity vs portability:

ECS EKS
Complexity Medium (AWS-native concepts) High (full Kubernetes)
Learning curve Moderate Steep
Portability AWS-only Multi-cloud, on-prem
Cost overhead No control plane charge $0.10/hour per cluster ($73/month)
Best for Most AWS-native teams Kubernetes-experienced teams, multi-cloud

Default to ECS unless your team already knows Kubernetes or you have a multi-cloud requirement. ECS is simpler, cheaper, and deeply integrated with AWS services.

Fargate vs EC2 Launch Type

ECS can run tasks on two types of compute:

Fargate (serverless containers)

  • No instances to manage
  • Per-vCPU and per-GB-hour pricing
  • Each task gets its own isolated environment
  • Scales by adding/removing tasks

EC2 (self-managed instances)

  • You manage the instance fleet (ASG, patching, capacity)
  • Traditional EC2 pricing (including spot)
  • Bin-packing multiple tasks per instance
  • More control (GPU instances, custom AMIs, local storage)

Use Fargate when: You want simplicity, don't need special instance types, and your workload fits in the Fargate size limits (4 vCPU / 30GB max, or 16 vCPU / 120GB with the large sizes).

Use EC2 when: You need spot instances, GPU workloads, specific instance types, or your task count is high enough that bin-packing is significantly cheaper.

Cost comparison

A 1 vCPU / 2GB Fargate task running 24/7:

  • Fargate: ~$30/month
  • EC2 (t3.micro, fits 1 task): ~$8/month

Fargate is 3-4x more expensive per unit of compute. You're paying for the operational simplicity. For most teams, the reduced ops burden is worth it.

When to Use Containers vs Lambda

Lambda ECS/Fargate
Startup time Cold start (100ms-3s) Always running (0ms)
Max duration 15 minutes Unlimited
Max memory 10GB 120GB
WebSocket No Yes
Pricing model Per-invocation Per-hour (running)
Scaling speed Instant (burst limits) Minutes (task startup)
Idle cost Zero Task minimum

Lambda for event-driven, short-duration, variable-traffic workloads. ECS/Fargate for always-on services, long-running workers, large workloads, or anything needing persistent connections.

Service Types

Services (long-running)

ECS keeps a desired number of tasks running. If one dies, it's replaced. Integrates with ALB for HTTP traffic. This is how you run APIs and web applications.

Tasks (one-off)

Run-to-completion jobs. Start a task, it processes data, it exits. Good for batch jobs, data migrations, scheduled work.

Scaling Patterns

Target tracking (simplest)

"Keep average CPU at 70%"

scaling.scaleOnCpuUtilization('CpuScaling', {
  targetUtilizationPercent: 70,
  scaleInCooldown: Duration.seconds(60),
  scaleOutCooldown: Duration.seconds(60),
});

Request-based scaling (ALB)

"Keep requests per target at 1000"

scaling.scaleOnRequestCount('RequestScaling', {
  requestsPerTarget: 1000,
  targetGroup: service.targetGroup,
});

Queue-based scaling

"Scale based on SQS queue depth"

scaling.scaleOnMetric('QueueScaling', {
  metric: queue.metricApproximateNumberOfMessagesVisible(),
  scalingSteps: [
    { upper: 0, change: -1 },    // scale in when empty
    { lower: 100, change: +1 },  // scale out at 100 messages
    { lower: 500, change: +3 },  // scale out faster at 500
  ],
});

Service Discovery

For microservices that need to find each other without an ALB:

AWS Cloud Map

Services register with a namespace. Other services resolve via DNS:

order-service.my-app.local β†’ 10.0.1.45, 10.0.2.67

No load balancer needed for internal service-to-service communication. Cheaper than ALB for internal traffic.

Service Connect

Newer ECS feature that handles service mesh functionality (load balancing, retries, observability) without sidecars or ALB for internal traffic.

CDK Example

import { Cluster, FargateService, ContainerImage } from 'aws-cdk-lib/aws-ecs';
import { ApplicationLoadBalancedFargateService } from 'aws-cdk-lib/aws-ecs-patterns';

// HTTP API service with ALB
const service = new ApplicationLoadBalancedFargateService(this, 'Api', {
  cluster,
  desiredCount: 2,
  taskImageOptions: {
    image: ContainerImage.fromAsset('./src/api'),
    containerPort: 8080,
    environment: { TABLE_NAME: table.tableName },
  },
  cpu: 512,
  memoryLimitMiB: 1024,
  runtimePlatform: {
    cpuArchitecture: CpuArchitecture.ARM64,
    operatingSystemFamily: OperatingSystemFamily.LINUX,
  },
  circuitBreaker: { rollback: true },
});

// Auto-scaling
const scaling = service.service.autoScaleTaskCount({
  minCapacity: 2,
  maxCapacity: 20,
});
scaling.scaleOnCpuUtilization('Cpu', { targetUtilizationPercent: 70 });

Deployment Strategies

Rolling update (default)

Replace tasks one at a time. Zero-downtime if you have 2+ tasks. Simple but slow rollbacks.

Blue/Green (with CodeDeploy)

Deploy to a new target group, shift traffic gradually (10% β†’ 50% β†’ 100%), rollback instantly if errors spike. Best for production APIs.

Circuit breaker

If new tasks fail health checks, ECS automatically rolls back to the previous task definition. Enable this always:

circuitBreaker: { rollback: true }

Further Reading

Looking for hands-on help? View my AWS architecture services β†’

Running containers on AWS?

Drop me a message β€” I typically respond within one business day.