A Complete Guide to Launch Templates for Auto Scaling Groups
A Launch Template is an AWS feature that provides a way to store launch parameters for EC2 instances, making it easier to launch instances with predefined configurations. Launch Templates contain all the parameters required to launch an EC2 instance, such as the AMI ID, instance type, security groups, and more.
| Feature | Launch Templates | Launch Configurations |
|---|---|---|
| Versioning | Yes | No |
| Modification | Create new version | Create new configuration |
| Spot Instances | Full support | Limited support |
| T2/T3 Unlimited | Supported | Not supported |
| Capacity Reservations | Supported | Not supported |
Launch Templates support versioning, allowing you to:
Steps to create a Launch Template via the AWS Console:
# Create a basic launch template
aws ec2 create-launch-template \
--launch-template-name "my-template" \
--version-description "Initial version" \
--launch-template-data '{
"ImageId": "ami-0abcdef1234567890",
"InstanceType": "t3.micro",
"KeyName": "my-key-pair",
"SecurityGroupIds": ["sg-0123456789abcdef0"],
"UserData": "IyEvYmluL2Jhc2gKZWNobyAiSGVsbG8gV29ybGQi"
}'
# Create a more complex launch template
aws ec2 create-launch-template \
--launch-template-name "web-server-template" \
--version-description "Web server config" \
--launch-template-data file://web-server-config.json
Resources:
MyLaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Properties:
LaunchTemplateName: my-web-server-template
VersionDescription: Initial version
LaunchTemplateData:
ImageId: ami-0abcdef1234567890
InstanceType: t3.micro
KeyName: my-key-pair
SecurityGroupIds:
- sg-0123456789abcdef0
UserData:
Fn::Base64: |
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
VolumeSize: 20
VolumeType: gp3
DeleteOnTermination: true
TagSpecifications:
- ResourceType: instance
Tags:
- Key: Name
Value: web-server
- ResourceType: volume
Tags:
- Key: Name
Value: web-server-volume
Steps to create an Auto Scaling Group using a Launch Template:
# Create an Auto Scaling Group with a Launch Template aws autoscaling create-auto-scaling-group \ --auto-scaling-group-name "my-asg" \ --launch-template "LaunchTemplateName=my-template,Version=1" \ --min-size 2 \ --max-size 10 \ --desired-capacity 2 \ --vpc-zone-identifier "subnet-0123456789abcdef0,subnet-0123456789abcdef1" \ --tags "Key=Environment,Value=Production,PropagateAtLaunch=true" # Update an existing ASG to use a new Launch Template version aws autoscaling update-auto-scaling-group \ --auto-scaling-group-name "my-asg" \ --launch-template "LaunchTemplateName=my-template,Version=2"
Configure a mix of instance types for cost optimization:
aws autoscaling create-auto-scaling-group \
--auto-scaling-group-name "mixed-instances-asg" \
--min-size 2 \
--max-size 10 \
--vpc-zone-identifier "subnet-0123456789abcdef0,subnet-0123456789abcdef1" \
--mixed-instances-policy '{
"LaunchTemplate": {
"LaunchTemplateSpecification": {
"LaunchTemplateName": "my-template",
"Version": "$Default"
},
"Overrides": [
{"InstanceType": "c5.large"},
{"InstanceType": "c5a.large"},
{"InstanceType": "m5.large"},
{"InstanceType": "m5a.large"}
]
},
"InstancesDistribution": {
"OnDemandBaseCapacity": 1,
"OnDemandPercentageAboveBaseCapacity": 25,
"SpotAllocationStrategy": "capacity-optimized"
}
}'
Steps to create a new version of an existing Launch Template:
# Create a new version via CLI
aws ec2 create-launch-template-version \
--launch-template-name "my-template" \
--version-description "Updated AMI" \
--source-version 1 \
--launch-template-data '{
"ImageId": "ami-0987654321fedcba0"
}'
Set a specific version as the default:
# Set a specific version as default aws ec2 modify-launch-template \ --launch-template-name "my-template" \ --default-version 2 # Get the default version aws ec2 describe-launch-templates \ --launch-template-names "my-template" \ --query "LaunchTemplates[0].DefaultVersionNumber" # List all versions of a template aws ec2 describe-launch-template-versions \ --launch-template-name "my-template"
Special version references:
Best practices for deploying new Launch Template versions:
Update the ASG to use a new template version and set instance refresh parameters to gradually replace instances.
aws autoscaling start-instance-refresh \
--auto-scaling-group-name "my-asg" \
--preferences '{"MinHealthyPercentage": 90, "InstanceWarmup": 300}'
Create a new ASG with the updated template, then gradually shift traffic from the old to the new ASG.
# Create new ASG with updated template aws autoscaling create-auto-scaling-group \ --auto-scaling-group-name "my-asg-green" \ --launch-template "LaunchTemplateName=my-template,Version=2" \ --min-size 0 \ --max-size 10 \ --desired-capacity 0 \ --vpc-zone-identifier "subnet-0123456789abcdef0,subnet-0123456789abcdef1"
Deploy a small percentage of instances with the new template version to test before full rollout.
# Create a mixed ASG with both versions
aws autoscaling create-auto-scaling-group \
--auto-scaling-group-name "canary-asg" \
--mixed-instances-policy '{
"LaunchTemplate": {
"LaunchTemplateSpecification": {
"LaunchTemplateName": "my-template",
"Version": "1"
},
"Overrides": [
{"LaunchTemplateSpecification": {"LaunchTemplateName": "my-template", "Version": "1"}},
{"LaunchTemplateSpecification": {"LaunchTemplateName": "my-template", "Version": "2"}}
]
},
"InstancesDistribution": {
"OnDemandPercentageAboveBaseCapacity": 100,
"SpotAllocationStrategy": "lowest-price"
}
}'
Configure a Launch Template for Spot Instances:
aws ec2 create-launch-template \
--launch-template-name "spot-template" \
--version-description "Spot configuration" \
--launch-template-data '{
"ImageId": "ami-0abcdef1234567890",
"InstanceType": "c5.large",
"SecurityGroupIds": ["sg-0123456789abcdef0"],
"InstanceMarketOptions": {
"MarketType": "spot",
"SpotOptions": {
"MaxPrice": "0.05",
"SpotInstanceType": "persistent",
"InstanceInterruptionBehavior": "stop"
}
}
}'
Optimize Spot usage with mixed instances policy:
aws autoscaling create-auto-scaling-group \
--auto-scaling-group-name "spot-optimized-asg" \
--min-size 2 \
--max-size 10 \
--vpc-zone-identifier "subnet-0123456789abcdef0,subnet-0123456789abcdef1" \
--mixed-instances-policy '{
"LaunchTemplate": {
"LaunchTemplateSpecification": {
"LaunchTemplateName": "spot-template",
"Version": "$Default"
},
"Overrides": [
{"InstanceType": "c5.large"},
{"InstanceType": "c5a.large"},
{"InstanceType": "c5n.large"},
{"InstanceType": "m5.large"},
{"InstanceType": "m5a.large"},
{"InstanceType": "r5.large"},
{"InstanceType": "r5a.large"}
]
},
"InstancesDistribution": {
"OnDemandBaseCapacity": 1,
"OnDemandPercentageAboveBaseCapacity": 0,
"SpotAllocationStrategy": "capacity-optimized",
"SpotInstancePools": 0
}
}'
Spot allocation strategies:
Configure instance bootstrapping with user data:
#!/bin/bash # Basic web server setup yum update -y yum install -y httpd systemctl start httpd systemctl enable httpd echo "Hello from $(hostname)" > /var/www/html/index.html
Adding user data to a Launch Template:
aws ec2 create-launch-template \
--launch-template-name "web-server-template" \
--version-description "Web server with user data" \
--launch-template-data '{
"ImageId": "ami-0abcdef1234567890",
"InstanceType": "t3.micro",
"SecurityGroupIds": ["sg-0123456789abcdef0"],
"UserData": "IyEvYmluL2Jhc2gKeXVtIHVwZGF0ZSAteQp5dW0gaW5zdGFsbCAteSBodHRwZApzeXN0ZW1jdGwgc3RhcnQgaHR0cGQKc3lzdGVtY3RsIGVuYWJsZSBodHRwZAplY2hvICJIZWxsbyBmcm9tICQoaG9zdG5hbWUpIiA+IC92YXIvd3d3L2h0bWwvaW5kZXguaHRtbA=="
}'
Note: User data must be base64 encoded when using the AWS CLI.
Use dynamic data in user scripts:
#!/bin/bash
# Get instance metadata
INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)
REGION=$(curl -s http://169.254.169.254/latest/meta-data/placement/region)
AZ=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone)
# Install AWS CLI
yum install -y aws-cli
# Tag EBS volumes
VOLUMES=$(aws ec2 describe-instances \
--region $REGION \
--instance-id $INSTANCE_ID \
--query "Reservations[0].Instances[0].BlockDeviceMappings[*].Ebs.VolumeId" \
--output text)
for VOLUME_ID in $VOLUMES; do
aws ec2 create-tags \
--region $REGION \
--resources $VOLUME_ID \
--tags Key=Name,Value=vol-$INSTANCE_ID
done
# Register with load balancer
aws elbv2 register-targets \
--region $REGION \
--target-group-arn arn:aws:elasticloadbalancing:$REGION:123456789012:targetgroup/my-targets/73e2d6bc24d8a067 \
--targets Id=$INSTANCE_ID
Use CloudFormation to create templates with dynamic user data:
Resources:
WebServerLaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Properties:
LaunchTemplateName: web-server-template
VersionDescription: Web server with dynamic user data
LaunchTemplateData:
ImageId: ami-0abcdef1234567890
InstanceType: t3.micro
SecurityGroupIds:
- !Ref WebServerSecurityGroup
UserData:
Fn::Base64: !Sub |
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
echo "Hello from ${AWS::StackName}" > /var/www/html/index.html
aws s3 cp s3://${ConfigBucket}/app.zip /tmp/
unzip /tmp/app.zip -d /var/www/html/
# Signal completion
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource WebServerASG --region ${AWS::Region}
Configure multiple ENIs in a Launch Template:
aws ec2 create-launch-template \
--launch-template-name "multi-eni-template" \
--version-description "Multiple ENIs" \
--launch-template-data '{
"ImageId": "ami-0abcdef1234567890",
"InstanceType": "c5.large",
"NetworkInterfaces": [
{
"DeviceIndex": 0,
"AssociatePublicIpAddress": true,
"DeleteOnTermination": true,
"Groups": ["sg-0123456789abcdef0"],
"SubnetId": "subnet-0123456789abcdef0"
},
{
"DeviceIndex": 1,
"DeleteOnTermination": true,
"Groups": ["sg-0123456789abcdef1"],
"SubnetId": "subnet-0123456789abcdef1"
}
]
}'
Use cases for multiple ENIs:
Enable enhanced networking in a Launch Template:
aws ec2 create-launch-template \
--launch-template-name "ena-template" \
--version-description "Enhanced networking" \
--launch-template-data '{
"ImageId": "ami-0abcdef1234567890",
"InstanceType": "c5.large",
"SecurityGroupIds": ["sg-0123456789abcdef0"],
"EnclaveOptions": {
"Enabled": false
},
"NetworkInterfaces": [
{
"DeviceIndex": 0,
"AssociatePublicIpAddress": true,
"DeleteOnTermination": true,
"Groups": ["sg-0123456789abcdef0"],
"InterfaceType": "efa"
}
]
}'
Enhanced networking options:
Configure Instance Metadata Service (IMDS) options:
aws ec2 create-launch-template \
--launch-template-name "imdsv2-template" \
--version-description "IMDSv2 required" \
--launch-template-data '{
"ImageId": "ami-0abcdef1234567890",
"InstanceType": "t3.micro",
"SecurityGroupIds": ["sg-0123456789abcdef0"],
"MetadataOptions": {
"HttpTokens": "required",
"HttpPutResponseHopLimit": 2,
"HttpEndpoint": "enabled"
}
}'
IMDSv2 security benefits:
Access instance metadata with IMDSv2:
#!/bin/bash
# Get IMDSv2 token
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
# Use token to get metadata
INSTANCE_ID=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/instance-id)
INSTANCE_TYPE=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/instance-type)
AZ=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/placement/availability-zone)
# Use metadata in configuration
echo "Instance ID: $INSTANCE_ID" > /var/www/html/instance-info.html
echo "Instance Type: $INSTANCE_TYPE" >> /var/www/html/instance-info.html
echo "Availability Zone: $AZ" >> /var/www/html/instance-info.html
Auto-scaling web application with Launch Templates:
Benefits of this approach:
# Create Launch Template for web servers
aws ec2 create-launch-template \
--launch-template-name "web-app-template" \
--version-description "Web application servers" \
--launch-template-data '{
"ImageId": "ami-0abcdef1234567890",
"InstanceType": "t3.medium",
"SecurityGroupIds": ["sg-0123456789abcdef0"],
"IamInstanceProfile": {
"Name": "WebAppInstanceProfile"
},
"BlockDeviceMappings": [
{
"DeviceName": "/dev/xvda",
"Ebs": {
"VolumeSize": 20,
"VolumeType": "gp3",
"DeleteOnTermination": true,
"Encrypted": true
}
}
],
"UserData": "IyEvYmluL2Jhc2gKIyBJbnN0YWxsIGRlcGVuZGVuY2llcwp5dW0gdXBkYXRlIC15CnltIGluc3RhbGwgLXkgaHR0cGQgcGhwIHBocC1teXNxbCBhd3MtY2xpCgojIEdldCBhcHBsaWNhdGlvbiBjb2RlCmF3cyBzMyBjcCBzMzovL215LWFwcC1idWNrZXQvYXBwLnppcCAvdG1wLwpjZCAvdmFyL3d3dy9odG1sCnVuemlwIC90bXAvYXBwLnppcAoKIyBDb25maWd1cmUgYXBwbGljYXRpb24KY2F0ID4gL3Zhci93d3cvaHRtbC9jb25maWcucGhwIDw8RU9GCjw/cGhwCiRkYl9ob3N0ID0gIiR7REJfSE9TVH0iOwokZGJfdXNlciA9ICIke0RCX1VTRVJ9IjsKJGRiX3Bhc3N3b3JkID0gIiR7REJfUEFTU1dPUkR9IjsKJGRiX25hbWUgPSAiJHtEQl9OQU1FfSI7CiRyZWdpb24gPSAiJHtSRUdJT059IjsKPz4KRU9GCgojIFN0YXJ0IHNlcnZpY2VzCnN5c3RlbWN0bCBzdGFydCBodHRwZApzeXN0ZW1jdGwgZW5hYmxlIGh0dHBkCgojIFJlZ2lzdGVyIHdpdGggbG9hZCBiYWxhbmNlcgpJTlNUQU5DRV9JRCA9ICQoY3VybCAtcyBodHRwOi8vMTY5LjI1NC4xNjkuMjU0L2xhdGVzdC9tZXRhLWRhdGEvaW5zdGFuY2UtaWQpClJFR0lPTiA9ICQoY3VybCAtcyBodHRwOi8vMTY5LjI1NC4xNjkuMjU0L2xhdGVzdC9tZXRhLWRhdGEvcGxhY2VtZW50L3JlZ2lvbikKYXdzIGVsYnYyIHJlZ2lzdGVyLXRhcmdldHMgLS1yZWdpb24gJFJFR0lPTiAtLXRhcmdldC1ncm91cC1hcm4gYXJuOmF3czplbGFzdGljbG9hZGJhbGFuY2luZzokUkVHSU9OOjEyMzQ1Njc4OTAxMjp0YXJnZXRncm91cC93ZWItdGFyZ2V0cy83M2UyZDZiYzI0ZDhhMDY3IC0tdGFyZ2V0cyBJZD0kSU5TVEFOQ0VfSUQ="
}'
# Create Auto Scaling Group
aws autoscaling create-auto-scaling-group \
--auto-scaling-group-name "web-app-asg" \
--launch-template "LaunchTemplateName=web-app-template,Version=$Default" \
--min-size 2 \
--max-size 10 \
--desired-capacity 2 \
--vpc-zone-identifier "subnet-0123456789abcdef0,subnet-0123456789abcdef1" \
--target-group-arns "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/web-targets/73e2d6bc24d8a067" \
--health-check-type "ELB" \
--health-check-grace-period 300 \
--tags "Key=Environment,Value=Production,PropagateAtLaunch=true"
Cost-optimized deployment with mixed instance types:
Benefits of this approach:
# Create Launch Template
aws ec2 create-launch-template \
--launch-template-name "cost-optimized-template" \
--version-description "Base template for mixed instances" \
--launch-template-data '{
"ImageId": "ami-0abcdef1234567890",
"SecurityGroupIds": ["sg-0123456789abcdef0"],
"IamInstanceProfile": {
"Name": "AppInstanceProfile"
},
"BlockDeviceMappings": [
{
"DeviceName": "/dev/xvda",
"Ebs": {
"VolumeSize": 20,
"VolumeType": "gp3",
"DeleteOnTermination": true
}
}
],
"UserData": "IyEvYmluL2Jhc2gKeXVtIHVwZGF0ZSAteQp5dW0gaW5zdGFsbCAteSBqYXZhLTEuOC4wLW9wZW5qZGsKYXdzIHMzIGNwIHMzOi8vbXktYXBwLWJ1Y2tldC9hcHAuamFyIC9ob21lL2VjMi11c2VyLwpqYXZhIC1qYXIgL2hvbWUvZWMyLXVzZXIvYXBwLmphciAtLXNlcnZlci5wb3J0PTgwODA="
}'
# Create Auto Scaling Group with mixed instances
aws autoscaling create-auto-scaling-group \
--auto-scaling-group-name "cost-optimized-asg" \
--min-size 4 \
--max-size 20 \
--vpc-zone-identifier "subnet-0123456789abcdef0,subnet-0123456789abcdef1,subnet-0123456789abcdef2" \
--mixed-instances-policy '{
"LaunchTemplate": {
"LaunchTemplateSpecification": {
"LaunchTemplateName": "cost-optimized-template",
"Version": "$Default"
},
"Overrides": [
{"InstanceType": "c5.large"},
{"InstanceType": "c5a.large"},
{"InstanceType": "c5n.large"},
{"InstanceType": "c4.large"},
{"InstanceType": "m5.large"},
{"InstanceType": "m5a.large"},
{"InstanceType": "m4.large"},
{"InstanceType": "r5.large"},
{"InstanceType": "r5a.large"},
{"InstanceType": "r4.large"}
]
},
"InstancesDistribution": {
"OnDemandBaseCapacity": 2,
"OnDemandPercentageAboveBaseCapacity": 20,
"SpotAllocationStrategy": "capacity-optimized",
"SpotInstancePools": 0
}
}'
Configure EC2-based ECS cluster with Launch Templates:
# Create Launch Template for ECS container instances
aws ec2 create-launch-template \
--launch-template-name "ecs-container-template" \
--version-description "ECS optimized instances" \
--launch-template-data '{
"ImageId": "ami-0abcdef1234567890",
"InstanceType": "c5.large",
"SecurityGroupIds": ["sg-0123456789abcdef0"],
"IamInstanceProfile": {
"Name": "ecsInstanceRole"
},
"BlockDeviceMappings": [
{
"DeviceName": "/dev/xvda",
"Ebs": {
"VolumeSize": 30,
"VolumeType": "gp3",
"DeleteOnTermination": true
}
}
],
"UserData": "IyEvYmluL2Jhc2gKZWNobyBFQ1NfQ0xVU1RFUj1teS1lY3MtY2x1c3RlciA+PiAvZXRjL2Vjcy9lY3MuY29uZmlnCmVjaG8gRUNTX0JBQ0tFTkRfSE9TVD0gPj4gL2V0Yy9lY3MvZWNzLmNvbmZpZwplY2hvIEVDU19FTkFCTEVfVEFTS19JQU1fUk9MRT10cnVlID4+IC9ldGMvZWNzL2Vjcy5jb25maWcKZWNobyBFQ1NfRU5BQkxFX1NQT1RfSU5TVEFOQ0VfRFJBSU5JTkc9dHJ1ZSA+PiAvZXRjL2Vjcy9lY3MuY29uZmlnCmVjaG8gRUNTX0VOQUJMRV9DT05UQUlORVJfTUVUQURBVEE9dHJ1ZSA+PiAvZXRjL2Vjcy9lY3MuY29uZmlnCmVjaG8gRUNTX0FWQUlMQUJMRV9MT0dHSU5HX0RSSVZFUlM9YXdzbG9ncyBmbHVlbnRkID4+IC9ldGMvZWNzL2Vjcy5jb25maWcKZWNobyBFQ1NfTlVNQl9DUFVfQ09SRV9TVVBQT1JUPXRydWUgPj4gL2V0Yy9lY3MvZWNzLmNvbmZpZw=="
}'
# Create Auto Scaling Group for ECS cluster
aws autoscaling create-auto-scaling-group \
--auto-scaling-group-name "ecs-container-asg" \
--launch-template "LaunchTemplateName=ecs-container-template,Version=$Default" \
--min-size 2 \
--max-size 10 \
--desired-capacity 2 \
--vpc-zone-identifier "subnet-0123456789abcdef0,subnet-0123456789abcdef1" \
--tags "Key=Name,Value=ECS-Container-Instance,PropagateAtLaunch=true"
Configure EKS node groups with custom Launch Templates:
# Create Launch Template for EKS nodes
aws ec2 create-launch-template \
--launch-template-name "eks-node-template" \
--version-description "EKS worker nodes" \
--launch-template-data '{
"ImageId": "ami-0abcdef1234567890",
"InstanceType": "m5.large",
"SecurityGroupIds": ["sg-0123456789abcdef0"],
"BlockDeviceMappings": [
{
"DeviceName": "/dev/xvda",
"Ebs": {
"VolumeSize": 50,
"VolumeType": "gp3",
"DeleteOnTermination": true,
"Encrypted": true
}
}
],
"MetadataOptions": {
"HttpTokens": "required",
"HttpPutResponseHopLimit": 2
},
"TagSpecifications": [
{
"ResourceType": "instance",
"Tags": [
{
"Key": "kubernetes.io/cluster/my-eks-cluster",
"Value": "owned"
}
]
}
]
}'
# Create EKS node group with the Launch Template
aws eks create-nodegroup \
--cluster-name my-eks-cluster \
--nodegroup-name custom-nodes \
--scaling-config minSize=3,maxSize=10,desiredSize=3 \
--subnets subnet-0123456789abcdef0 subnet-0123456789abcdef1 \
--node-role arn:aws:iam::123456789012:role/EKSNodeRole \
--launch-template id=lt-0123456789abcdef0,version=1
| Feature | Launch Templates | Launch Configurations | CloudFormation | EC2 Run Instances |
|---|---|---|---|---|
| Versioning | Yes | No | Yes (Stack) | No |
| Reusability | High | Medium | High | Low |
| Auto Scaling Integration | Native | Native | Via resources | Manual |
| Spot Instance Support | Full | Limited | Full | Basic |
| Mixed Instance Types | Yes | No | Yes | No |
| Infrastructure as Code | Supported | Supported | Native | Limited |