
Trying Out Launch Before Terminate, a New Feature of Amazon EC2 Auto Scaling
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.
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.
