ECS services running a fixed task count with no Application Auto Scaling target
What does ZopNight detect here?
An ECS service with no registered scalable target runs the same task count at 3 a.m. as at peak, so capacity is sized for the worst hour and paid for in every hour. ZopNight reads the autoscaling_enabled flag discovery derives from DescribeScalableTargets and fires only on a confirmed false.
Signal and threshold
| Field | Value |
|---|---|
| Rule IDs | RC-160 |
| Category | compliance |
| Severity | medium |
| Metric | none — pure configuration read |
| Source | ecs_autoscaling.go |
Where it applies
Fixed task counts fail in both directions
A service pinned at N tasks is wrong twice a day: over-provisioned through every quiet hour (paying Fargate or EC2 rates for headroom nobody uses) and under-provisioned the moment traffic exceeds what N can serve, with no mechanism to recover except a human and a deploy. Auto scaling is filed under compliance here rather than cost because the posture is the finding. The dollar impact varies with the traffic curve, and this rule does not pretend to measure it. No savings figure is attached, and none is fabricated.
How ZopNight knows scaling is absent
Discovery asks Application Auto Scaling for scalable targets registered against the service and writes a boolean: at least one target found, or none. The rule fires only when the flag is present and false. If the enrichment was denied or failed, the flag is simply absent and the rule abstains. A service is never accused of a missing scaling policy that discovery could not actually verify. The signal is definitive when present, because a registered scalable target is precisely what makes ECS scaling exist.
See which services are covered
aws application-autoscaling describe-scalable-targets \ --service-namespace ecs \ --query 'ScalableTargets[].[ResourceId,MinCapacity,MaxCapacity]' --output tableServices missing from this list have no scaling configuration at all; compare against
aws ecs list-services --cluster <name>.
Configuring it without drama
Target tracking is the low-risk default: pick CPU utilization from the cluster’s CloudWatch series or ALB request count per target, set a target value around 60–70%, and set the floor and ceiling deliberately: the floor is your availability promise, the ceiling is your cost cap. Step scaling and scheduled scaling exist for spiky or predictable patterns, but most services never need them. Test by replaying a load spike in staging and watching scale-out and, just as importantly, scale-in behaviour.
Where a fixed count is legitimate
Queue workers whose concurrency is licensed, singleton schedulers, and services with strict warm-capacity contracts sometimes run fixed counts on purpose. For those, the honest response to this finding is a documented decision rather than a config change, and a scheduled-scaling policy that at least drops the count out of hours is often still available.