TL;DR: Without auto scaling you pick a task count for your worst hour and pay for it 24/7 — if the daily peak needs 8 tasks and the nightly trough needs 2, flat-8 means 75% of your tasks are idle most of the time. ECS Service Auto Scaling (free, since 2016) tracks task count to real demand and is often the single largest cost lever in ECS — bigger than rightsizing or Spot. Two rules make or break it: scale queue workers on queue-depth-per-task, not CPU, and scale out fast, scale in slow.
The numbers
- Three policies: target tracking ("keep CPU ~70%", easiest), step scaling (graduated thresholds), scheduled scaling (known daily shape) — the pro move is target-tracking + scheduled together.
- Metric choice is everything: CPU/RequestCountPerTarget for web APIs (RequestCountPerTarget leads load); queue-depth-per-task (
ApproximateNumberOfMessagesVisible÷ running tasks, target ~100) for SQS workers — CPU lags a backlog. - Asymmetric cooldowns: scale out fast (slow = angry users), scale in slow (~5 min, slow = a few extra task-minutes); prevents saw-tooth "flapping."
- Field examples: image workers moved CPU→messages-per-task for ~50% off at equal throughput; a launch-spiky API on RequestCountPerTarget scaled 2→8 in under a minute before CPU climbed; a bootstrapped SaaS combined scheduled (min 4 business hours / 1 off-hours) + CPU backstop for ~60% off in 30 minutes of config.
Do this
- Turn on scaling with sensible bounds — min=2 (redundancy floor), max=10 (cost circuit breaker); run a week, then adjust from the actual task-count graph.
- Pick the metric that leads your load — CPU/RequestCountPerTarget for web, queue-depth-per-task for SQS workers (keep min=1 to avoid cold-start on the first message).
- Set a ~5-minute scale-in cooldown to stop flapping; keep scale-out responsive.
- Layer Cluster Auto Scaling under EC2-launch-type services — scaling tasks does nothing if you run out of EC2 nodes; Fargate provisions compute for you.
- Combine scheduled + target tracking for predictable-shape days: schedule the known pattern, let target tracking catch surprises; graduate to custom business metrics (active sessions, concurrent viewers) for the real wins.
Gotchas
- Scaling on the wrong metric — CPU on queue workers means you're always a step behind the backlog; memory on stateless APIs misleads.
- Forgetting the max — a runaway loop or traffic flood can push task count (and the bill) to absurd levels; the max is a cost circuit breaker.
- No cooldown → "flapping bills" — a saw-tooth task-count graph means the scale-in cooldown is too short.
- EC2 launch type needs two-layer scaling — task scaling silently stalls waiting for nodes without Cluster Auto Scaling.
Skip this if
- The service genuinely serves flat 24/7 load with no trough — there's nothing to scale down into; commit the baseline with Compute Savings Plans instead.
- The workload is time-bound batch rather than load-bound — AWS Batch with Spot orchestrates it natively. Rightsize each task first with ECS Fargate Task Rightsizing, then cut the per-task rate on background workers with Fargate Spot.