Right-Sizing Task CPU & Memory

Stop guessing task sizes — use real utilization to pick CPU and memory that fit, then leave a sensible headroom.

The single biggest source of wasted ECS spend is over-provisioned tasks. Someone once picked 1024 CPU and 2048 memory because it felt safe, and three years later nobody has questioned it. Right-sizing is the highest-leverage habit you can build.

How I approach it

  1. Turn on Container Insights so you get per-task CPU and memory utilization, not just cluster averages.
  2. Look at the p95 of memory usage over a busy week — not the average, and not the single peak.
  3. Set the task memory to roughly p95 plus 20–30% headroom. Memory is a hard limit: cross it and the kernel OOM-kills your container, so you want a real buffer here.
  4. CPU is a softer, burstable resource, so you can run it closer to the bone. Size CPU near p95 and let brief spikes borrow idle cycles.

The Fargate wrinkle

Fargate only accepts fixed CPU/memory pairs, so you cannot dial in an arbitrary number. Round to the nearest valid pair — and remember that CPU choice constrains the memory range, so sometimes the cheapest legal fit is one notch up on CPU.

Memory: size for safety. CPU: size for cost. They are not symmetric, and treating them the same is how you either burn money or get OOM-killed at 3am.

Example

Example · json
{
  "family": "api",
  "requiresCompatibilities": [
    "FARGATE"
  ],
  "networkMode": "awsvpc",
  "cpu": "512",
  "memory": "1024",
  "containerDefinitions": [
    {
      "name": "api",
      "image": "123456789012.dkr.ecr.us-east-1.amazonaws.com/api:42",
      "essential": true,
      "memoryReservation": 768,
      "portMappings": [
        {
          "containerPort": 8080
        }
      ]
    }
  ]
}

When to use it

  • A FinOps team uses Container Insights CPU and memory utilization data to downsize 30% of services that were provisioned with default values and never profiled.
  • A developer sets task CPU to 256 and memory to 512 after load-testing confirms the container peaks at 180 MB and 15% of one vCPU under production traffic.
  • An SRE adds 20% headroom above the P99 memory peak so tasks are never OOM-killed during traffic spikes while still avoiding gross over-provisioning.

More examples

Fetch P99 Memory for a Service

Retrieves the daily peak memory for the last 7 days so you can size the task memory limit to P99 peak plus a 20% buffer.

Example · bash
aws cloudwatch get-metric-statistics \
  --namespace ECS/ContainerInsights \
  --metric-name MemoryUtilized \
  --dimensions Name=ClusterName,Value=prod Name=ServiceName,Value=api \
  --start-time $(date -u -d '-7 days' +%FT%TZ) \
  --end-time $(date -u +%FT%TZ) \
  --period 86400 \
  --statistics Maximum \
  --output table

Update Task Memory After Right-Sizing

Reads the current task definition and re-registers it with only the memory field updated to the right-sized value.

Example · bash
# Fetch current task def and change only memory
CURRENT=$(aws ecs describe-task-definition \
  --task-definition api \
  --query 'taskDefinition' --output json)

echo "$CURRENT" | jq '.memory = "768"' | \
  aws ecs register-task-definition --cli-input-json file:///dev/stdin

Enable Container Insights for Sizing Data

Enables Container Insights so per-container CPU and memory metrics become available for right-sizing analysis.

Example · bash
aws ecs update-cluster-settings \
  --cluster prod \
  --settings name=containerInsights,value=enabled

echo "Container Insights enabled — check CloudWatch in ~5 minutes"

Discussion

  • Be the first to comment on this lesson.