# Fix AWS Auto Scaling Not Triggering
Your Auto Scaling group isn't scaling when it should. Traffic spikes aren't triggering scale-out events, or unused capacity isn't scaling in. The metrics clearly show the conditions are met, but nothing happens. Understanding why Auto Scaling policies don't trigger requires examining the metrics, policies, cooldown periods, and group configuration.
Diagnosis Commands
First, check the Auto Scaling group status:
aws autoscaling describe-auto-scaling-groups \
--auto-scaling-group-name my-asg \
--query 'AutoScalingGroups[*].[AutoScalingGroupName,DesiredCapacity,MinSize,MaxSize,Instances[*].[InstanceId,HealthStatus,LifecycleState]]'Get scaling policies:
aws autoscaling describe-policies \
--auto-scaling-group-name my-asg \
--query 'ScalingPolicies[*].[PolicyName,PolicyType,AdjustmentType,ScalingAdjustment,MetricAggregationType,TargetTrackingConfig]'Check recent scaling activities:
aws autoscaling describe-scaling-activities \
--auto-scaling-group-name my-asg \
--max-items 20 \
--query 'Activities[*].[ActivityId,Time,StatusCode,Description,Cause]'Check CloudWatch alarms attached to policies:
aws autoscaling describe-policies \
--auto-scaling-group-name my-asg \
--query 'ScalingPolicies[*].[PolicyName,Alarms[*].[AlarmName]]'Verify alarm states:
aws cloudwatch describe-alarms \
--alarm-names my-cpu-alarm \
--query 'MetricAlarms[*].[AlarmName,StateValue,StateReason]'Get metrics for the group:
aws cloudwatch get-metric-statistics \
--namespace AWS/AutoScaling \
--metric-name GroupInServiceInstances \
--dimensions Name=AutoScalingGroupName,Value=my-asg \
--start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
--period 300 \
--statistics Average \
--output tableCheck EC2 metrics (CPU for target tracking):
aws cloudwatch get-metric-statistics \
--namespace AWS/EC2 \
--metric-name CPUUtilization \
--dimensions Name=AutoScalingGroupName,Value=my-asg \
--start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
--period 60 \
--statistics Average,Maximum \
--output tableCommon Causes and Solutions
Scaling Policy Not Attached
The policy exists but isn't associated with the Auto Scaling group:
aws autoscaling describe-policies \
--auto-scaling-group-name my-asg \
--query 'ScalingPolicies[*].AutoScalingGroupName'If empty or no policies returned, create a policy:
# Target tracking policy
aws autoscaling put-scaling-policy \
--auto-scaling-group-name my-asg \
--policy-name cpu-target-tracking \
--policy-type TargetTrackingScaling \
--target-tracking-configuration file://target-tracking.jsonWhere target-tracking.json contains:
{
"TargetValue": 50.0,
"PredefinedMetricSpecification": {
"PredefinedMetricType": "ASGAverageCPUUtilization"
},
"ScaleOutCooldown": 300,
"ScaleInCooldown": 300
}Min/Max Limits Reached
The group is at its minimum or maximum size:
aws autoscaling describe-auto-scaling-groups \
--auto-scaling-group-name my-asg \
--query 'AutoScalingGroups[*].[DesiredCapacity,MinSize,MaxSize]'If DesiredCapacity equals MaxSize, it won't scale out. If equals MinSize, it won't scale in.
Adjust limits:
aws autoscaling update-auto-scaling-group \
--auto-scaling-group-name my-asg \
--min-size 2 \
--max-size 20Cooldown Period Active
Cooldown periods prevent rapid scaling:
aws autoscaling describe-auto-scaling-groups \
--auto-scaling-group-name my-asg \
--query 'AutoScalingGroups[*].[DefaultCooldown]'And check policy cooldowns:
aws autoscaling describe-policies \
--auto-scaling-group-name my-asg \
--query 'ScalingPolicies[*].[PolicyName,TargetTrackingConfig.ScaleOutCooldown,TargetTrackingConfig.ScaleInCooldown]'Default cooldown is 300 seconds. During cooldown, new scaling activities are blocked.
Reduce cooldown for faster response:
```bash aws autoscaling update-auto-scaling-group \ --auto-scaling-group-name my-asg \ --default-cooldown 60
# For target tracking aws autoscaling put-scaling-policy \ --auto-scaling-group-name my-asg \ --policy-name cpu-target-tracking \ --policy-type TargetTrackingScaling \ --target-tracking-configuration '{"TargetValue":50,"PredefinedMetricSpecification":{"PredefinedMetricType":"ASGAverageCPUUtilization"},"ScaleOutCooldown":60,"ScaleInCooldown":120}' ```
Metric Not Available
The metric Auto Scaling watches doesn't exist or has insufficient data:
aws cloudwatch get-metric-statistics \
--namespace AWS/EC2 \
--metric-name CPUUtilization \
--dimensions Name=AutoScalingGroupName,Value=my-asg \
--start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
--period 60 \
--statistics AverageIf no data points returned, the metric isn't being published. Enable detailed monitoring:
aws autoscaling enable-metrics-collection \
--auto-scaling-group-name my-asg \
--granularity 1MinuteBasic monitoring (5-minute granularity) might be too coarse for fast scaling.
Wrong Metric Dimensions
For EC2 metrics, use instance-level not group-level dimensions:
Target tracking with CPU uses AutoScalingGroupName dimension automatically. For custom metrics:
aws autoscaling put-scaling-policy \
--auto-scaling-group-name my-asg \
--policy-name custom-metric-tracking \
--policy-type TargetTrackingScaling \
--target-tracking-configuration '{"TargetValue":100,"CustomizedMetricSpecification":{"MetricName":"RequestCount","Namespace":"MyApp","Dimensions":[{"Name":"Service","Value":"WebApp"}],"Statistic":"Sum","Unit":"Count"},"ScaleOutCooldown":300}'Ensure dimensions match exactly what's published:
aws cloudwatch list-metrics \
--namespace MyApp \
--metric-name RequestCount \
--query 'Metrics[*].Dimensions'Scale-In Protection Enabled
Instances have scale-in protection preventing termination:
aws autoscaling describe-auto-scaling-groups \
--auto-scaling-group-name my-asg \
--query 'AutoScalingGroups[*].Instances[*].[InstanceId,ProtectedFromScaleIn]'Remove protection if needed:
aws autoscaling set-instance-protection \
--instance-ids i-12345 \
--auto-scaling-group-name my-asg \
--no-protected-from-scale-inTarget Value Not Appropriate
The target value is unrealistic for your workload:
aws autoscaling describe-policies \
--auto-scaling-group-name my-asg \
--query 'ScalingPolicies[*].TargetTrackingConfig.TargetValue'If target is 70% but your typical CPU is 30%, scaling won't trigger often.
Adjust target value:
aws autoscaling put-scaling-policy \
--auto-scaling-group-name my-asg \
--policy-name cpu-target-tracking \
--policy-type TargetTrackingScaling \
--target-tracking-configuration '{"TargetValue":40,"PredefinedMetricSpecification":{"PredefinedMetricType":"ASGAverageCPUUtilization"}}'Instance Health Issues
Unhealthy instances prevent proper scaling:
aws autoscaling describe-auto-scaling-groups \
--auto-scaling-group-name my-asg \
--query 'AutoScalingGroups[*].Instances[*].[InstanceId,HealthStatus]'Auto Scaling replaces unhealthy instances but doesn't count them for scaling decisions.
Fix health issues:
```bash # Check ELB target health aws elbv2 describe-target-health \ --target-group-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/my-tg/abc123 \ --query 'TargetHealthDescriptions[*].[Target.Id,TargetHealth.State]'
# Set health status manually if needed aws autoscaling set-instance-health \ --instance-id i-12345 \ --health-status Healthy ```
Standby Instances
Instances in standby aren't counted for scaling:
aws autoscaling describe-auto-scaling-groups \
--auto-scaling-group-name my-asg \
--query 'AutoScalingGroups[*].Instances[*].[InstanceId,LifecycleState]'States:
- InService: Active, counted for scaling
- Standby: Not counted, manual intervention needed
- Pending/Terminating: Transitioning
Exit standby:
aws autoscaling exit-standby \
--instance-ids i-12345 \
--auto-scaling-group-name my-asgSuspended Processes
Scaling processes might be suspended:
aws autoscaling describe-auto-scaling-groups \
--auto-scaling-group-name my-asg \
--query 'AutoScalingGroups[*].SuspendedProcesses[*].[ProcessName,SuspensionReason]'Processes that can be suspended:
- Launch: Prevents new instances
- Terminate: Prevents instance termination
- HealthCheck: No health checking
- ReplaceUnhealthy: No replacement of unhealthy instances
- AZRebalance: No AZ rebalancing
- AlarmNotification: Ignores CloudWatch alarms
- ScheduledActions: Ignores scheduled actions
Resume suspended processes:
aws autoscaling resume-processes \
--auto-scaling-group-name my-asg \
--scaling-processes Launch Terminate AlarmNotificationWarmup Period for Target Tracking
For warm instances, warmup affects when they're counted:
aws autoscaling describe-auto-scaling-groups \
--auto-scaling-group-name my-asg \
--query 'AutoScalingGroups[*].[WarmPoolConfiguration]'If warm pool is configured, new instances go through warmup before being counted. This delays scaling.
Adjust warm pool settings:
aws autoscaling put-warm-pool \
--auto-scaling-group-name my-asg \
--max-group-warm-capacity 5 \
--warm-pool-min-size 2Multiple Conflicting Policies
Multiple policies can conflict:
aws autoscaling describe-policies \
--auto-scaling-group-name my-asg \
--query 'ScalingPolicies[*].[PolicyName,PolicyType,TargetTrackingConfig.TargetValue]'If you have multiple target tracking policies, only the most aggressive one for scale-out takes effect.
Remove conflicting policies:
aws autoscaling delete-policy \
--policy-name conflicting-policy \
--auto-scaling-group-name my-asgVerification Steps
Test scaling by forcing conditions:
```bash # For CPU-based scaling, increase load # SSH into instance and stress CPU stress --cpu 4 --timeout 300
# Or use a load testing tool hey -z 5m -c 100 http://my-alb.example.com/ ```
Monitor scaling activity:
```bash # Watch for scaling activities aws autoscaling describe-scaling-activities \ --auto-scaling-group-name my-asg \ --max-items 5 \ --query 'Activities[*].[Time,StatusCode,Description]'
# Check instance count aws autoscaling describe-auto-scaling-groups \ --auto-scaling-group-name my-asg \ --query 'AutoScalingGroups[*].[DesiredCapacity,Instances]' ```
Create diagnostic script:
```bash #!/bin/bash ASG_NAME="my-asg"
echo "Auto Scaling Diagnostics" echo "========================"
echo "1. Group Configuration:" aws autoscaling describe-auto-scaling-groups \ --auto-scaling-group-names $ASG_NAME \ --query 'AutoScalingGroups[*].[AutoScalingGroupName,DesiredCapacity,MinSize,MaxSize,DefaultCooldown]'
echo "" echo "2. Current Instances:" aws autoscaling describe-auto-scaling-groups \ --auto-scaling-group-names $ASG_NAME \ --query 'AutoScalingGroups[*].Instances[*].[InstanceId,HealthStatus,LifecycleState,ProtectedFromScaleIn]'
echo "" echo "3. Scaling Policies:" aws autoscaling describe-policies \ --auto-scaling-group-name $ASG_NAME \ --query 'ScalingPolicies[*].[PolicyName,PolicyType,AdjustmentType,ScalingAdjustment,TargetTrackingConfig.TargetValue]'
echo "" echo "4. Suspended Processes:" aws autoscaling describe-auto-scaling-groups \ --auto-scaling-group-names $ASG_NAME \ --query 'AutoScalingGroups[*].SuspendedProcesses[*].ProcessName'
echo "" echo "5. Recent Scaling Activities:" aws autoscaling describe-scaling-activities \ --auto-scaling-group-name $ASG_NAME \ --max-items 5 \ --query 'Activities[*].[Time,StatusCode,Description,Cause]'
echo "" echo "6. CloudWatch Alarm States:" aws cloudwatch describe-alarms \ --alarm-names $(aws autoscaling describe-policies --auto-scaling-group-name $ASG_NAME --query 'ScalingPolicies[*].Alarms[*].AlarmName' --output text) \ --query 'MetricAlarms[*].[AlarmName,StateValue,StateReason]'
echo "" echo "7. CPU Utilization (last 30 minutes):" aws cloudwatch get-metric-statistics \ --namespace AWS/EC2 \ --metric-name CPUUtilization \ --dimensions Name=AutoScalingGroupName,Value=$ASG_NAME \ --start-time $(date -u -d '30 minutes ago' +%Y-%m-%dT%H:%M:%SZ) \ --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \ --period 60 \ --statistics Average,Maximum \ --output table ```
Set up monitoring for scaling failures:
```bash # Alarm for instances at max capacity aws cloudwatch put-metric-alarm \ --alarm-name asg-at-max-capacity \ --alarm-description "ASG at maximum capacity" \ --namespace AWS/AutoScaling \ --metric-name GroupInServiceInstances \ --dimensions Name=AutoScalingGroupName,Value=$ASG_NAME \ --statistic Average \ --period 60 \ --threshold $(aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names $ASG_NAME --query 'AutoScalingGroups[0].MaxSize' --output text) \ --comparison-operator GreaterThanOrEqualToThreshold \ --evaluation-periods 5 \ --alarm-actions arn:aws:sns:us-east-1:123456789012:alerts
# Alarm for scaling failures aws cloudwatch put-metric-alarm \ --alarm-name scaling-failures \ --alarm-description "Auto Scaling activities failed" \ --namespace AWS/AutoScaling \ --metric-name GroupTotalInstances \ --dimensions Name=AutoScalingGroupName,Value=$ASG_NAME \ --statistic SampleCount \ --period 300 \ --threshold 0 \ --comparison-operator LessThanThreshold \ --evaluation-periods 1 \ --treat-missing-data breaching ```