# 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:

bash
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:

bash
aws autoscaling describe-policies \
  --auto-scaling-group-name my-asg \
  --query 'ScalingPolicies[*].[PolicyName,PolicyType,AdjustmentType,ScalingAdjustment,MetricAggregationType,TargetTrackingConfig]'

Check recent scaling activities:

bash
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:

bash
aws autoscaling describe-policies \
  --auto-scaling-group-name my-asg \
  --query 'ScalingPolicies[*].[PolicyName,Alarms[*].[AlarmName]]'

Verify alarm states:

bash
aws cloudwatch describe-alarms \
  --alarm-names my-cpu-alarm \
  --query 'MetricAlarms[*].[AlarmName,StateValue,StateReason]'

Get metrics for the group:

bash
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 table

Check EC2 metrics (CPU for target tracking):

bash
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 table

Common Causes and Solutions

Scaling Policy Not Attached

The policy exists but isn't associated with the Auto Scaling group:

bash
aws autoscaling describe-policies \
  --auto-scaling-group-name my-asg \
  --query 'ScalingPolicies[*].AutoScalingGroupName'

If empty or no policies returned, create a policy:

bash
# 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.json

Where target-tracking.json contains:

json
{
  "TargetValue": 50.0,
  "PredefinedMetricSpecification": {
    "PredefinedMetricType": "ASGAverageCPUUtilization"
  },
  "ScaleOutCooldown": 300,
  "ScaleInCooldown": 300
}

Min/Max Limits Reached

The group is at its minimum or maximum size:

bash
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:

bash
aws autoscaling update-auto-scaling-group \
  --auto-scaling-group-name my-asg \
  --min-size 2 \
  --max-size 20

Cooldown Period Active

Cooldown periods prevent rapid scaling:

bash
aws autoscaling describe-auto-scaling-groups \
  --auto-scaling-group-name my-asg \
  --query 'AutoScalingGroups[*].[DefaultCooldown]'

And check policy cooldowns:

bash
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:

bash
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

If no data points returned, the metric isn't being published. Enable detailed monitoring:

bash
aws autoscaling enable-metrics-collection \
  --auto-scaling-group-name my-asg \
  --granularity 1Minute

Basic 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:

bash
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:

bash
aws cloudwatch list-metrics \
  --namespace MyApp \
  --metric-name RequestCount \
  --query 'Metrics[*].Dimensions'

Scale-In Protection Enabled

Instances have scale-in protection preventing termination:

bash
aws autoscaling describe-auto-scaling-groups \
  --auto-scaling-group-name my-asg \
  --query 'AutoScalingGroups[*].Instances[*].[InstanceId,ProtectedFromScaleIn]'

Remove protection if needed:

bash
aws autoscaling set-instance-protection \
  --instance-ids i-12345 \
  --auto-scaling-group-name my-asg \
  --no-protected-from-scale-in

Target Value Not Appropriate

The target value is unrealistic for your workload:

bash
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:

bash
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:

bash
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:

bash
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:

bash
aws autoscaling exit-standby \
  --instance-ids i-12345 \
  --auto-scaling-group-name my-asg

Suspended Processes

Scaling processes might be suspended:

bash
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:

bash
aws autoscaling resume-processes \
  --auto-scaling-group-name my-asg \
  --scaling-processes Launch Terminate AlarmNotification

Warmup Period for Target Tracking

For warm instances, warmup affects when they're counted:

bash
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:

bash
aws autoscaling put-warm-pool \
  --auto-scaling-group-name my-asg \
  --max-group-warm-capacity 5 \
  --warm-pool-min-size 2

Multiple Conflicting Policies

Multiple policies can conflict:

bash
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:

bash
aws autoscaling delete-policy \
  --policy-name conflicting-policy \
  --auto-scaling-group-name my-asg

Verification 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 ```