Skip to content
AWS aws load-balancing 6 min read

Creating an Auto Scaling Group

An Auto Scaling Group (ASG) is the AWS service that automatically launches and terminates EC2 instances (the virtual servers you rent in AWS) to match the load on your application. Instead of running a fixed number of servers, you tell the ASG how few and how many instances you want, and it keeps the right number running, replaces ones that fail, and spreads them across data centers for resilience. This page walks through creating an ASG end to end — choosing a launch template, picking subnets in multiple Availability Zones, setting the desired/min/max sizes, and attaching it to a load balancer’s target group so traffic flows to every instance the group launches.

What you need before you start

Creating an ASG depends on a couple of building blocks already existing. Get these ready first:

  • A launch template — a saved blueprint that describes what to launch: the AMI (Amazon Machine Image, the disk snapshot your server boots from), the instance type (e.g. t3.micro), the security group, the key pair, and any startup script. The ASG reads this template every time it needs a new instance.
  • A VPC with subnets in multiple Availability Zones — a VPC (Virtual Private Cloud, your private network in AWS) and at least two subnets, each in a different Availability Zone (an AZ is an isolated data center within a region). Spreading across AZs is what makes the group highly available.
  • (Optional but common) A target group — the list of instances that an Application Load Balancer (ALB) forwards traffic to. Attaching the ASG to a target group is what wires scaling to the load balancer.

Launch templates, not launch configurations. Launch configurations are the legacy mechanism — AWS no longer accepts new ones since the end of 2023. Always use launch templates: they support versioning, newer instance types, mixed instances, and Spot purchasing.

Step 1 — Create a launch template

A launch template is the reusable definition of an instance. You usually create it once and reference it from the ASG.

Console steps:

  1. Open the EC2 console and choose Launch Templates in the left menu, then Create launch template.
  2. Give it a name, e.g. web-app-lt.
  3. Under Application and OS Images, pick an AMI (e.g. Amazon Linux 2023).
  4. Choose an instance type (e.g. t3.micro).
  5. Select a key pair for SSH access and a security group (e.g. sg-0a1b2c3d) that allows traffic from your load balancer.
  6. Expand Advanced details and paste a startup script in User data if you want the instance to install software on boot.
  7. Choose Create launch template.

CLI equivalent:

aws ec2 create-launch-template \
  --launch-template-name web-app-lt \
  --version-description v1 \
  --launch-template-data '{
    "ImageId": "ami-0abcdef1234567890",
    "InstanceType": "t3.micro",
    "SecurityGroupIds": ["sg-0a1b2c3d"],
    "KeyName": "my-key"
  }'

Output:

{
    "LaunchTemplate": {
        "LaunchTemplateId": "lt-0a1b2c3d4e5f6789a",
        "LaunchTemplateName": "web-app-lt",
        "DefaultVersionNumber": 1,
        "LatestVersionNumber": 1
    }
}

Step 2 — Understand desired, minimum, and maximum capacity

Every ASG has three numbers that control how many instances run:

SettingWhat it meansWhen it matters
MinimumThe fewest instances the group will ever run, even at zero traffic.Sets your baseline cost and availability floor.
MaximumThe most instances the group is allowed to launch.A safety cap so a traffic spike (or a bug) can’t run up a huge bill.
DesiredThe number the ASG actively tries to maintain right now.Scaling policies move this value up and down between min and max.

For a typical web tier you might set min = 2 (so a single AZ failure never drops you to zero), desired = 2, and max = 6. The ASG never goes below the minimum or above the maximum, no matter what a scaling policy requests.

Step 3 — Create the Auto Scaling Group

Now you tie everything together: the template, the subnets across AZs, the capacity numbers, and (optionally) the target group.

Console steps:

  1. In the EC2 console, choose Auto Scaling Groups, then Create Auto Scaling group.
  2. Name it (e.g. web-app-asg) and select your launch template web-app-lt. Pick the template version (Latest is common).
  3. On the network step, choose your VPC (vpc-0a1b2c3d) and select two or more subnets in different Availability Zones (e.g. subnet-0a1b2c3d in us-east-1a and subnet-0b2c3d4e in us-east-1b).
  4. On the load balancing step, choose Attach to an existing load balancer, then Choose from your load balancer target groups, and select your target group (e.g. web-tg). Turn on Turn on Elastic Load Balancing health checks.
  5. Set Desired capacity = 2, Minimum = 2, Maximum = 6.
  6. (Optional) Add a scaling policy now, or leave it for later. Review and choose Create Auto Scaling group.

CLI equivalent:

aws autoscaling create-auto-scaling-group \
  --auto-scaling-group-name web-app-asg \
  --launch-template "LaunchTemplateName=web-app-lt,Version=\$Latest" \
  --min-size 2 \
  --max-size 6 \
  --desired-capacity 2 \
  --vpc-zone-identifier "subnet-0a1b2c3d,subnet-0b2c3d4e" \
  --target-group-arns "arn:aws:elasticloadbalancing:us-east-1:111122223333:targetgroup/web-tg/abc123def456" \
  --health-check-type ELB \
  --health-check-grace-period 120

This command returns no output on success. Verify it like this:

aws autoscaling describe-auto-scaling-groups \
  --auto-scaling-group-names web-app-asg \
  --query 'AutoScalingGroups[0].Instances[].{Id:InstanceId,AZ:AvailabilityZone,State:LifecycleState}'

Output:

[
    {
        "Id": "i-0a1b2c3d4e5f6789a",
        "AZ": "us-east-1a",
        "State": "InService"
    },
    {
        "Id": "i-0b2c3d4e5f6789ab",
        "AZ": "us-east-1b",
        "State": "InService"
    }
]

Notice the two instances landed in different AZs — the ASG balances new instances across the subnets you gave it. If you had listed only one subnet, both instances would sit in a single data center and an AZ outage would take your whole app down.

Why the target group attachment matters

Listing --target-group-arns is the step that connects scaling to your load balancer. When the ASG launches a new instance, it automatically registers that instance in the target group, so the ALB starts sending it traffic once it passes health checks. When the ASG terminates an instance during scale-in, it deregisters it first. Without this attachment the ASG would still launch instances, but no traffic would ever reach them.

Setting --health-check-type ELB also tells the ASG to trust the load balancer’s health checks: if the ALB marks an instance unhealthy, the ASG replaces it. The --health-check-grace-period gives a new instance time (here 120 seconds) to boot and warm up before health checks count against it.

Infrastructure as Code

For repeatable, reviewable infrastructure, define the ASG in CloudFormation or Terraform rather than clicking the console.

WebAppAsg:
  Type: AWS::AutoScaling::AutoScalingGroup
  Properties:
    AutoScalingGroupName: web-app-asg
    MinSize: "2"
    MaxSize: "6"
    DesiredCapacity: "2"
    VPCZoneIdentifier:
      - subnet-0a1b2c3d
      - subnet-0b2c3d4e
    LaunchTemplate:
      LaunchTemplateId: lt-0a1b2c3d4e5f6789a
      Version: !GetAtt WebAppLt.LatestVersionNumber
    TargetGroupARNs:
      - !Ref WebTargetGroup
    HealthCheckType: ELB
    HealthCheckGracePeriod: 120

Cost note

You pay for the EC2 instances the ASG runs — the ASG service itself is free. Two t3.micro On-Demand instances in us-east-1 cost roughly $15/month combined; your bill scales up only when a policy adds instances during busy periods and scales back down afterward. Setting a sensible maximum is your main guardrail against a runaway bill. Using Spot Instances through a mixed-instances policy can cut compute cost by up to 70-90% for fault-tolerant workloads.

Best Practices

  • Always span at least two Availability Zones so a single data-center failure can’t take down your whole group.
  • Use launch templates (never legacy launch configurations) and reference a specific or $Latest version explicitly.
  • Set health-check-type to ELB when behind a load balancer so unhealthy instances are replaced based on real application health, not just EC2 status.
  • Pick a health check grace period long enough for your instance to finish booting and bootstrapping, or healthy instances will be killed prematurely.
  • Keep the maximum size realistic to cap cost and blast radius, and set minimum size high enough to survive losing one AZ.
  • Bake as much setup as possible into the AMI and keep user-data scripts short, so new instances become healthy faster during scale-out.
Last updated June 15, 2026
Was this helpful?