Symptoms
- Instances go
InService, fail health checks, terminate, and new ones launch — over and over. - Capacity never stabilises; the load balancer has few or no healthy targets.
- It often starts right after a new AMI, launch template version or instance refresh.
1. Read the scaling activities
aws autoscaling describe-scaling-activities --auto-scaling-group-name asg-web --max-items 15 \
--query "Activities[].{time:StartTime, status:StatusCode, cause:Cause, desc:Description}" --output table
| Cause text mentions | Meaning |
|---|---|
ELB system health check failure | Target group health check fails |
EC2 health check ... impaired | Instance status check fails (boot, kernel, disk) |
Launching a new EC2 instance. Status Reason: ... with an error | Launch fails (quota, capacity, AMI, IAM, subnet IPs) |
instance refresh | A refresh is replacing instances with a bad version |
2. Freeze replacements while you investigate
aws autoscaling suspend-processes --auto-scaling-group-name asg-web \
--scaling-processes ReplaceUnhealthy HealthCheck AZRebalance
Healthy-or-not instances now stay, so you can log in and look. Remember to resume later.
3. Health check failures
aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names asg-web \
--query "AutoScalingGroups[0].{type:HealthCheckType, grace:HealthCheckGracePeriod, lt:LaunchTemplate}"
aws elbv2 describe-target-health --target-group-arn <tg-arn> \
--query "TargetHealthDescriptions[].{id:Target.Id, state:TargetHealth.State, reason:TargetHealth.Reason, desc:TargetHealth.Description}" --output table
- Grace period too short: the app needs longer to start than
HealthCheckGracePeriod. Raise it (for example 300s) or use lifecycle hooks / warm pools. - Health check path wrong after a release:
Target.ResponseCodeMismatchorTarget.Timeout. - Security group: instances must accept the health check port from the load balancer's security group.
On an instance, check user data and app logs:
sudo cat /var/log/cloud-init-output.log | tail -50
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8080/health
4. Roll back the change
Point the ASG at the last good launch template version:
aws ec2 describe-launch-template-versions --launch-template-id lt-0abc --query "LaunchTemplateVersions[].[VersionNumber,CreateTime,VersionDescription]" --output table
aws autoscaling update-auto-scaling-group --auto-scaling-group-name asg-web \
--launch-template LaunchTemplateId=lt-0abc,Version=12
If an instance refresh is in progress:
aws autoscaling cancel-instance-refresh --auto-scaling-group-name asg-web
aws autoscaling rollback-instance-refresh --auto-scaling-group-name asg-web
Then start a new refresh to the good version if needed.
5. Resume
aws autoscaling resume-processes --auto-scaling-group-name asg-web
Warning
Resume only after targets are healthy. Resuming
ReplaceUnhealthywith sick instances restarts the loop.
Prevent it next time
- Instance refresh with checkpoints and auto rollback and alarms tied to it.
- Grace period based on measured start-up time; readiness endpoints that return 200 only when the app is ready.
- Test new AMIs in a canary ASG before production.