The usual problem
MinSize is set to the daily peak "to be safe", because scaling out took 8 minutes the one time it was tried. The fix is to make scaling fast and trustworthy, then lower the floor.
1. Make instances ready faster
| Step | Fix |
|---|---|
| Long user data installing packages | Bake a golden AMI (EC2 Image Builder); user data only for config |
| App start-up slow | Profile it; warm caches lazily |
| Health check grace too long/short | Set HealthCheckGracePeriod to real boot time; use ALB health checks |
| Default instance warm-up unset | Set DefaultInstanceWarmup so new instances' metrics don't distort scaling |
Measure: time from launch to InService and to healthy in the target group.
2. Target tracking on the right metric
aws autoscaling put-scaling-policy --auto-scaling-group-name web-asg --policy-name rps-target \
--policy-type TargetTrackingScaling --target-tracking-configuration '{
"PredefinedMetricSpecification": {"PredefinedMetricType": "ALBRequestCountPerTarget",
"ResourceLabel": "app/web-alb/50dc6c495c0c9188/targetgroup/web-tg/943f017f100becff"},
"TargetValue": 800.0
}'
| Workload | Good metric |
|---|---|
| Web behind ALB | ALBRequestCountPerTarget (find the per-instance rate where latency starts rising) |
| CPU-bound processing | ASGAverageCPUUtilization ~50–60% |
| Queue workers | Custom metric: backlog per instance (queue depth ÷ instances) |
Avoid stacking several step policies that fight each other.
3. Predictive scaling for daily patterns
Predictive scaling forecasts load from history and launches capacity ahead of the curve. Start in forecast only mode, compare with actual load for a week, then switch to forecast and scale:
aws autoscaling put-scaling-policy --auto-scaling-group-name web-asg --policy-name predictive \
--policy-type PredictiveScaling --predictive-scaling-configuration '{
"MetricSpecifications": [{"TargetValue": 800,
"PredefinedMetricPairSpecification": {"PredefinedMetricType": "ALBRequestCount",
"ResourceLabel": "app/web-alb/50dc6c495c0c9188/targetgroup/web-tg/943f017f100becff"}}],
"Mode": "ForecastOnly", "SchedulingBufferTime": 300 }'
Keep target tracking alongside it for unexpected spikes.
4. Warm pools for slow-booting apps
A warm pool keeps pre-initialised instances stopped (or hibernated), so scale-out resumes them instead of booting from scratch. You pay for stopped instances' EBS, not compute:
aws autoscaling put-warm-pool --auto-scaling-group-name web-asg --pool-state Stopped --min-size 2 \
--instance-reuse-policy '{"ReuseOnScaleIn": true}'
Use lifecycle hooks so app initialisation completes before an instance goes into the warm pool.
5. Mixed instances and spot
"MixedInstancesPolicy": {
"InstancesDistribution": {
"OnDemandBaseCapacity": 2,
"OnDemandPercentageAboveBaseCapacity": 30,
"SpotAllocationStrategy": "price-capacity-optimized"
},
"LaunchTemplate": {
"LaunchTemplateSpecification": {"LaunchTemplateName": "web-lt", "Version": "$Latest"},
"Overrides": [{"InstanceRequirements": {"VCpuCount": {"Min": 2, "Max": 4}, "MemoryMiB": {"Min": 4096}}}]
}
}
- Attribute-based instance selection (
InstanceRequirements) gives many spot pools automatically. - Enable Capacity Rebalancing so ASG replaces at-risk spot instances proactively.
- Keep a small on-demand base for steady capacity.
6. Lower the floor
Once scale-out is fast and tested (load test from minimum to peak), reduce MinSize to what's needed for HA (e.g. 2 across AZs) plus a buffer for sudden spikes, and let scaling do the rest.
7. Scale-in safely
- Connection draining: target group deregistration delay set to real request duration (the default 300 s is often too long).
- Instance scale-in protection for instances running long jobs.
- Termination policies that keep AZs balanced.
Tip
Run a load test that starts at the new minimum and ramps to peak faster than real traffic. If latency SLOs hold, the lower floor is safe; if not, you know exactly which step is slow.
Checklist
- Launch-to-healthy time measured and reduced
- Target tracking on a meaningful metric
- Predictive scaling tested in forecast-only first
- Warm pool for slow-booting apps
- Mixed instances with spot diversification and rebalancing
- MinSize lowered after a ramp test
- Scale-in draining and protection tuned