Trying Out Launch Before Terminate, a New Feature of Amazon EC2 Auto Scaling

Trying Out Launch Before Terminate, a New Feature of Amazon EC2 Auto Scaling

I enabled Amazon EC2 Auto Scaling Launch Before Terminate at the account level and checked instance counts during a refresh. With no setting specified, old instances ended only after new ones were healthy, and healthy targets never fell below 4.
2026.10.28

This page has been translated by machine translation. View original

Introduction

In October 2026, Amazon EC2 Auto Scaling made it possible to set Launch Before Terminate as the default replacement behavior at the account level.

https://aws.amazon.com/about-aws/whats-new/2026/10/ec2-auto-scaling-launch-before-terminate/

When Launch Before Terminate is enabled, all ASGs in the account launch the replacement instance first, and terminate the instance being replaced only after the replacement passes its health check. It applies to replacements caused by instance refresh, rebalancing, health checks, and the maximum instance lifetime being reached, and there is reportedly no additional charge.

This time, I checked the difference in behavior between when Launch Before Terminate is enabled and when it is disabled, using an instance refresh with no Preferences specified on an ASG in the Tokyo region.

Account settings

The setting name is launchBeforeTerminate, and the values are enabled and disabled.

aws autoscaling describe-account-settings
{
    "Settings": [
        {
            "Name": "launchBeforeTerminate",
            "Value": "disabled",
            "EnforcementType": "none"
        }
    ]
}

These are the commands to enable it and to revert it to disabled.

aws autoscaling put-account-setting --name launchBeforeTerminate --value enabled
aws autoscaling put-account-setting --name launchBeforeTerminate --value disabled

Test environment

In the default VPC in the Tokyo region (three public subnets), I created an ALB and an ASG with CloudFormation.

  • Instances: t4g.nano, Amazon Linux 2023 arm64, running Nginx
  • ASG: minimum 4, desired 4, maximum 8. Health check type is ELB, grace period is 60 seconds. No instance maintenance policy
  • Target group: health check path is /, interval 10 seconds, healthy threshold 2, unhealthy threshold 2. Deregistration delay is 30 seconds
  • AWS CLI: aws-cli/2.38.6
Full template and deploy command
AWSTemplateFormatVersion: '2010-09-09'
Description: ASG Launch Before Terminate account setting verification (ALB + ASG of t4g.nano running Nginx). Delete the stack after the test.

Parameters:
  VpcId:
    Type: AWS::EC2::VPC::Id
  SubnetIds:
    Type: List<AWS::EC2::Subnet::Id>
    Description: Public subnets in at least two AZs (default VPC)
  AmiId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64
  InstanceType:
    Type: String
    Default: t4g.nano

Resources:
  AlbSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: ALB (test, HTTP from anywhere)
      VpcId: !Ref VpcId
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0

  InstanceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: EC2 (HTTP from ALB only)
      VpcId: !Ref VpcId
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          SourceSecurityGroupId: !Ref AlbSecurityGroup

  LoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Type: application
      Scheme: internet-facing
      Subnets: !Ref SubnetIds
      SecurityGroups:
        - !Ref AlbSecurityGroup
      LoadBalancerAttributes:
        - Key: deletion_protection.enabled
          Value: 'false'

  TargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      VpcId: !Ref VpcId
      Protocol: HTTP
      Port: 80
      TargetType: instance
      HealthCheckPath: /
      HealthCheckIntervalSeconds: 10
      HealthCheckTimeoutSeconds: 5
      HealthyThresholdCount: 2
      UnhealthyThresholdCount: 2
      Matcher:
        HttpCode: '200'
      TargetGroupAttributes:
        - Key: deregistration_delay.timeout_seconds
          Value: '30'

  Listener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref LoadBalancer
      Protocol: HTTP
      Port: 80
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref TargetGroup

  LaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateData:
        ImageId: !Ref AmiId
        InstanceType: !Ref InstanceType
        SecurityGroupIds:
          - !Ref InstanceSecurityGroup
        MetadataOptions:
          HttpTokens: required
        UserData:
          Fn::Base64: |
            #!/bin/bash
            # t4g.nano (0.5 GiB) gets OOM-killed during dnf, so add swap first
            dd if=/dev/zero of=/swapfile bs=1M count=1024
            chmod 600 /swapfile
            mkswap /swapfile
            swapon /swapfile
            dnf install -y nginx
            TOKEN=$(curl -s -X PUT http://169.254.169.254/latest/api/token -H 'X-aws-ec2-metadata-token-ttl-seconds: 60')
            IID=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/instance-id)
            echo "instance-id: $IID" > /usr/share/nginx/html/index.html
            systemctl enable --now nginx

  AutoScalingGroup:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      MinSize: '4'
      DesiredCapacity: '4'
      MaxSize: '8'
      VPCZoneIdentifier: !Ref SubnetIds
      LaunchTemplate:
        LaunchTemplateId: !Ref LaunchTemplate
        Version: !GetAtt LaunchTemplate.LatestVersionNumber
      TargetGroupARNs:
        - !Ref TargetGroup
      HealthCheckType: ELB
      HealthCheckGracePeriod: 60
      Tags:
        - Key: Name
          Value: devio-asg-lbt
          PropagateAtLaunch: true
        - Key: Project
          Value: devio-asg-lbt
          PropagateAtLaunch: true

Outputs:
  AlbDnsName:
    Value: !GetAtt LoadBalancer.DNSName
  AutoScalingGroupName:
    Value: !Ref AutoScalingGroup
  TargetGroupArn:
    Value: !Ref TargetGroup
aws cloudformation deploy --stack-name devio-asg-lbt --template-file template.yaml --parameter-overrides VpcId=<vpc-id> SubnetIds=<subnet-id-1>\,<subnet-id-2>\,<subnet-id-3> --tags Project=devio-asg-lbt --no-fail-on-empty-changeset

Measurement results

I started an instance refresh.

aws autoscaling start-instance-refresh --auto-scaling-group-name <asg-name>

The instance counts show only the rows where they changed. The total is the number of instances in the ASG, including those being terminated. For the events, I extracted one replacement caused by the instance refresh from the output of describe-scaling-activities. Times are in JST.

aws autoscaling describe-scaling-activities --auto-scaling-group-name <asg-name>

disabled

時刻      合計  InService  healthy
13:16:32   4     4          4
13:16:36   ← 古いインスタンスを終了
13:16:38   4     3          3
13:16:38   ← 新しいインスタンスを起動
13:16:44   5     4          3
13:17:49   4     4          3
13:17:55   4     4          4

Termination came first, and the launch started about 2 seconds later. InService and healthy targets dropped to 3. InService returned to 4 after 6 seconds, but healthy targets stayed at 3 for about 77 seconds, until 13:17:55 when the new instance became healthy.

enabled

時刻      合計  InService  healthy
12:58:05   5     4          4
12:58:06   ← 新しいインスタンスを起動
12:58:11   5     5          4
12:59:22   5     5          5
13:00:44   ← 古いインスタンスを終了
13:00:45   5     4          4
13:00:45   ← 次の新しいインスタンスを起動
13:00:51   6     5          4

The launch came first, and termination started about 2 minutes 38 seconds later. Termination started after 12:59:22, when the healthy targets reached 5. All four replacements followed the same order.

In the console, "Manage launch before terminate" states that for groups with no instance maintenance policy configured, a minimum healthy percentage of 100% and a maximum healthy percentage of 110% are applied. The Preferences of the refresh with nothing specified while enabled were also MinHealthyPercentage 100 and MaxHealthyPercentage 110.

Throughout the entire refresh, InService and healthy targets never fell below 4 when enabled. Instead, during replacement the total increased to a maximum of 6 (InService up to a maximum of 5). The ASG maximum was set to 8, and I did not try an ASG whose maximum equals its desired capacity.

Summary

Enabling Launch Before Terminate in the EC2 Auto Scaling account settings makes it easier to avoid incidents where, during maintenance that replaces instances, a failure to launch the replacement instance reduces the number of effective instances.

This was a single measurement, but I was able to confirm that with an instance refresh with nothing specified, the old instance is terminated only after the new instance becomes healthy.

For accounts with many ASGs, this seems to be an effective setting as a measure to protect production environments. However, because the number of running EC2 instances temporarily increases, costs may rise and maintenance may take longer. If you use On-Demand Capacity Reservations or strictly manage licenses, please also be mindful that the reserved capacity may run short or the license count may be exceeded.

Share this article

AWSのお困り事はクラスメソッドへ