bigonotes
CloudCompute

EC2 Auto Scaling Groups

How an Auto Scaling group keeps enough healthy instances: launch templates, scaling policies, lifecycle hooks and termination.

9 min readLast updated

An Auto Scaling group (ASG) is a set of identical EC2 instances that AWS manages as one unit. You tell it how many instances you want and what each one looks like. The ASG launches them, replaces unhealthy ones, and adds or removes capacity as load changes. This note covers how ASGs work on AWS; for the general idea of autoscaling and how to pick signals, see Autoscaling.

The building blocks#

An ASG needs three things: a recipe for instances, a size range, and a place to run.

Launch template#

A launch template describes each instance: the AMI, instance type, security groups, key pair, IAM role, user data and metadata options. Templates are versioned, so you can roll forward or back by changing which version the ASG uses.

You may still see the older option in existing accounts. Launch configurations are the older, unversioned way to do this. They are deprecated and get no new features. Use launch templates for all new groups.

Capacity: min, max and desired#

With the recipe set, the next question is how many instances to run. Three numbers control the group's size.

Setting Meaning
Minimum The group never goes below this count.
Maximum The group never goes above this count.
Desired The count the group works to reach right now.

Desired capacity must sit between min and max; the API rejects a value outside that range. Scaling policies change the desired count, and the ASG launches or terminates instances to match it.

For example, with min 2, desired 4 and max 6: if one instance fails a health check, the ASG terminates it and launches a replacement, so the group returns to 4.

Regions and Availability Zones#

The last building block is where the instances run. An ASG belongs to one Region and can span several Availability Zones in it. You list one subnet per zone. The ASG spreads instances evenly across those zones, so losing one zone does not take the service down. An ASG cannot span Regions; for multi-Region setups you run one ASG per Region and route between them with DNS.

Health checks and replacement#

Keeping the desired count only works if the ASG knows which instances are broken. It checks every instance and replaces the ones that fail. It can use two kinds of checks:

  • EC2 status checks (always on): detect a dead host or an unreachable OS.
  • Elastic Load Balancing health checks (opt in): detect an app that is up but failing its health endpoint. Turn these on when the group sits behind a load balancer.

When an instance is marked unhealthy, the ASG terminates it and launches a new one to keep desired capacity.

Health check grace period#

Health checks have one catch: a new instance needs time to boot and run its user data before it can pass a health check. The health check grace period tells the ASG to ignore failing checks for that long after launch. If the period is too short, the ASG kills instances that are still starting, and the group can loop forever replacing them.

Load balancer integration#

Most ASGs sit behind a load balancer, which is why the ELB checks above matter. You attach an ASG to one or more load balancer target groups. The ASG then registers each new instance with the target group and deregisters each instance before it terminates. During deregistration, the load balancer stops sending new requests and lets in-flight ones finish (connection draining); Load Balancing covers how the balancer side works.

Scaling policies#

So far the group holds a fixed desired count. A scaling policy is what changes that count as load moves, and EC2 Auto Scaling supports several kinds.

Policy How it works Use it when
Target tracking Keeps a metric near a target, like a thermostat (for example average CPU at 50%). Default choice for most services.
Step scaling Changes capacity by different amounts for different alarm breaches. You want bigger jumps for bigger spikes.
Simple scaling One alarm, one adjustment, then waits out a cooldown. Legacy; prefer target tracking or step.
Scheduled Sets min, max or desired at set times (cron). Load follows a known clock, like business hours.
Predictive Forecasts load from past daily and weekly patterns and scales ahead of it. Regular cycles where boot time hurts.

You can also scale manually by setting desired capacity yourself. Policies can be combined; for example, scheduled actions raise the floor before a known event while target tracking handles the rest.

Choosing a metric#

Target tracking and step scaling both watch a metric, so the metric you pick decides how well they work. Good metrics rise and fall with load and recover when you add instances.

  • Average CPU utilization works for CPU-bound services.
  • ALB request count per target works well for web services behind an Application Load Balancer.
  • Custom metrics, such as queue backlog per instance, work for worker fleets.

Metrics come from Amazon CloudWatch. Target tracking creates and manages the CloudWatch alarms for you.

The commands below put the pieces together. They create a group across two subnets behind a target group, then attach a target tracking policy whose config is the JSON file that follows.

# An ASG across two subnets, behind a target group, using ELB checks.
aws autoscaling create-auto-scaling-group \
  --auto-scaling-group-name web-asg \
  --launch-template "LaunchTemplateName=web-lt,Version=\$Latest" \
  --min-size 2 --max-size 6 --desired-capacity 2 \
  --vpc-zone-identifier "subnet-0aaa1111,subnet-0bbb2222" \
  --target-group-arns "$TARGET_GROUP_ARN" \
  --health-check-type ELB --health-check-grace-period 120

# Target tracking: keep average CPU near 50%.
aws autoscaling put-scaling-policy \
  --auto-scaling-group-name web-asg --policy-name cpu50 \
  --policy-type TargetTrackingScaling \
  --target-tracking-configuration file://cpu50.json
{
  "PredefinedMetricSpecification": {
    "PredefinedMetricType": "ASGAverageCPUUtilization"
  },
  "TargetValue": 50.0
}

Cooldowns and instance warm-up#

A new instance does not help right away. Without a pause, the ASG would see the metric still high and keep adding instances it does not need. Two settings prevent this.

  • Cooldown applies to simple scaling policies. After a scaling activity, the group waits for the cooldown before the policy can act again. Scheduled actions ignore it.
  • Instance warm-up applies to target tracking and step scaling. A new instance's metrics do not count toward the group's aggregate until it has warmed up, and the ASG accounts for instances still warming when it decides whether to add more.

Set warm-up to roughly how long an instance takes to boot and start serving. A golden AMI shortens this time.

Lifecycle hooks and instance states#

Sometimes an instance needs work done before it serves traffic or before it disappears. A lifecycle hook pauses an instance at launch or termination so you can run custom actions. During the pause, the instance waits in a Wait state until your code calls complete-lifecycle-action or the hook's timeout expires.

The diagram and table show where the hooks fit in an instance's life.

Auto Scaling instance states: Pending, Pending:Wait, Pending:Proceed, InService, then Terminating, Terminating:Wait, Terminating:Proceed, Terminated, with Standby as a side state of InService
State What is happening
Pending The instance is launching.
Pending:Wait A launch hook paused it, for example to install software or warm a cache.
Pending:Proceed The hook finished; the instance moves on.
InService Healthy and serving traffic.
Standby Still in the group but out of service, for maintenance or debugging.
Terminating:Wait A termination hook paused it, for example to drain work or ship logs.
Terminating:Proceed Cleanup is done; termination continues.
Terminated Gone; it never returns to the group.

There is also Detaching, for an instance you remove from the group without terminating it. Hook events reach your code through EventBridge, SNS or SQS. If the timeout expires, the hook applies its default result: continue or abandon.

Of those states, Standby is the one you'll use by hand, mostly for troubleshooting. You move an instance to standby, the load balancer stops sending it traffic, and you inspect it. You can choose whether the ASG launches a replacement while it is out. When you are done, you return it to service.

Scale-in and termination policies#

Lifecycle hooks handle cleanup once an instance is chosen to go. Which instance goes is a separate decision. When the group shrinks, a termination policy chooses. The default policy works in this order:

  1. Pick the Availability Zone with the most instances, to keep zones balanced.
  2. Within it, prefer instances that do not fit the group's allocation strategy (for mixed On-Demand and Spot groups).
  3. Then prefer instances using the oldest launch template or configuration.
  4. Then prefer instances closest to their next billing hour.
  5. Break any remaining tie at random.

You can choose other built-in policies, such as OldestInstance, NewestInstance or OldestLaunchTemplate, or write a custom one with a Lambda function. Scale-in protection marks instances the ASG must not terminate on scale-in, such as a worker in the middle of a long job.

Updating a running group#

To ship a new AMI or config, create a new launch template version. Then start an instance refresh, which replaces instances in batches while keeping a minimum share healthy. This is a rolling deploy for the group (see Deployment Strategies).

Instance refresh isn't the only way a group's makeup changes. A group can also mix instance types and purchase options, for example a base of On-Demand plus extra Spot capacity across several types.

Pitfalls#

Most ASG incidents trace back to a few settings.

  • Grace period too short: instances fail checks while booting and get replaced in a loop.
  • EC2 checks only behind a load balancer: a crashed app on a live host stays in service. Turn on ELB health checks.
  • Max set too low: the group hits the ceiling during a spike and stops scaling.
  • State on instances: scale-in terminates instances and their local data. Keep instances stateless.
  • Flapping: a thin gap between scale-out and scale-in triggers makes the group bounce. Target tracking scales in more slowly than it scales out to avoid this.

Key takeaways#

  • An ASG keeps desired capacity between min and max, replacing unhealthy instances automatically.
  • Use launch templates; launch configurations are deprecated.
  • ASGs are regional and spread instances across Availability Zones.
  • Prefer target tracking; add scheduled or predictive scaling for known patterns.
  • Grace period, cooldown and warm-up stop the group from acting on instances that are still starting.
  • Lifecycle hooks pause instances in Pending:Wait or Terminating:Wait for setup and cleanup.
Revision notes · 2 min

Core idea#

An Auto Scaling group (ASG) keeps a set of identical EC2 instances at the desired count. It replaces unhealthy instances and changes capacity as load changes.

Key points#

  • A launch template defines each instance and is versioned. Launch configurations are deprecated.
  • Desired capacity must sit between min and max. Policies change desired; the ASG launches or terminates to match.
  • An ASG is regional and spreads instances across Availability Zones, one subnet per zone. It cannot span Regions.
  • Health checks: EC2 status checks always; add ELB health checks behind a load balancer.
  • The health check grace period ignores failing checks while a new instance boots.
  • Attached target groups register new instances and deregister leaving ones, with connection draining.
  • Instance refresh rolls a new launch template version out in batches.

Scaling policies#

Policy Use it when
Target tracking Default; keep a metric near a target
Step scaling Bigger jumps for bigger breaches
Simple scaling Legacy; one alarm, then cooldown
Scheduled Load follows a known clock
Predictive Regular daily or weekly cycles
  • Good metrics: average CPU, ALB request count per target, queue backlog per instance.
  • Cooldown applies to simple scaling. Instance warm-up applies to target tracking and step scaling.

Lifecycle states#

State Meaning
Pending:Wait Launch hook pause for setup
InService Healthy and serving
Standby In the group, out of service
Terminating:Wait Termination hook pause for cleanup
Terminated Gone for good
  • Each Wait ends with complete-lifecycle-action or a timeout, then moves to Proceed.

Termination policy (default)#

  • Zone with the most instances first.
  • Then instances that break the allocation strategy.
  • Then the oldest launch template or configuration.
  • Then closest to the next billing hour, then random.
  • Scale-in protection shields chosen instances.

Pitfalls#

  • A short grace period causes a replace loop.
  • EC2 checks alone miss a crashed app on a live host.
  • A low max stops scaling during a spike.
  • Local state is lost on scale-in. Keep instances stateless.

Say it in an interview#

  • "A multi-AZ ASG behind an ALB, target tracking on request count per target, ELB health checks."
  • "Lifecycle hooks drain work before termination."

Found a mistake? Email ask@bigonotes.com

Further reading#