We spun up a single AWS Spot instance using Terraform to slash our cloud bill. We covered how to deploy AWS Spot Instances using Terraform, but if you're running anything important, a single Spot instance is risky. AWS can reclaim that server at any moment with just a two-minute warning.
If your server dies, your app goes down.
To fix this, we don't stop using Spot instances — we find a better way. Today, we're going to build a self-healing Auto Scaling Group (ASG) using Terraform. We will configure it to maintain 100% Spot capacity, automatically pick the cheapest availability zones, and install a web server on boot so you can prove it actually works.
Now open your terminal and get ready for this session.
Set Up Your Project
Create a new directory for this project and create your main.tf file. We are using us-east-1 (you can choose your own region). This region has the deepest Spot capacity pools, which means fewer interruptions.
mkdir aws-spot-asg && cd aws-spot-asg nano main.tf
The Foundation and Firewall
Before we spin up servers, we need to tell Terraform which AWS region to use, grab the latest Amazon Linux image, and open port 80 for web traffic and port 22 for SSH.
Paste this whole code into main.tf. Replace your region in this code.
provider "aws" {
region = "us-east-1"
}
# Grab the latest Amazon Linux 2023 image
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["al2023-ami-2023.*-x86_64"]
}
}
# Grab your default VPC automatically
data "aws_vpc" "default" {
default = true
}
data "aws_subnets" "default" {
filter {
name = "vpc-id"
values = [data.aws_vpc.default.id]
}
}
# Allow HTTP and SSH traffic
resource "aws_security_group" "web_sg" {
name_prefix = "web-sg-"
vpc_id = data.aws_vpc.default.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
Then save the file.
The Launch Template
An Auto Scaling Group needs a blueprint to know exactly what to build when a new server is required.
We are going to use a user_data script to automatically update the OS, install Apache, and write a simple HTML file on boot.
Add this right below your security group in main.tf:
# The blueprint for our instances
resource "aws_launch_template" "spot_app" {
name_prefix = "spot-app-template-"
image_id = data.aws_ami.amazon_linux.id
instance_type = "t3.micro"
vpc_security_group_ids = [aws_security_group.web_sg.id]
# Install Apache and write a custom homepage
user_data = base64encode(<<-EOF
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
echo "Hello from my AWS Spot Instance!" > /var/www/html/index.html
EOF
)
}
Then save the file.
The Auto Scaling Group
Here is where it all comes together. We are going to use mixed_instances_policy. This tells AWS: "I want exactly two servers running. Use 100% Spot instances to save money, but if t3.micro is out of stock, fall back to t3.small."
We are also using the capacity-based allocation strategy. AWS will check all available Spot pools and pick the ones that are least likely to be interrupted.
Add this final block at the bottom of your file main.tf:
# The Auto Scaling Group
resource "aws_autoscaling_group" "spot_fleet" {
name = "spot-asg-example"
vpc_zone_identifier = data.aws_subnets.default.ids
# How many servers we want running
desired_capacity = 2
max_size = 3
min_size = 1
mixed_instances_policy {
instances_distribution {
on_demand_base_capacity = 0
on_demand_percentage_above_base_capacity = 0 # 100% Spot instances
spot_allocation_strategy = "capacity-optimized"
}
launch_template {
launch_template_specification {
launch_template_id = aws_launch_template.spot_app.id
version = "$Latest"
}
# Fallback options if capacity is tight
override { instance_type = "t3.micro" }
override { instance_type = "t3.small" }
}
}
}
# Output the running instances so we can test them
data "aws_instances" "spot_servers" {
filter {
name = "tag:aws:autoscaling:groupName"
values = [aws_autoscaling_group.spot_fleet.name]
}
filter {
name = "instance-state-name"
values = ["running"]
}
}
output "instance_public_ips" {
value = data.aws_instances.spot_servers.public_ips
}
Then save this file.
Fixing the Credentials Error
If you want to run Terraform and you run the terraform apply command, you may sometimes see errors like the ones below.
Error: No valid credential sources found
Error: failed to refresh cached credentials, no EC2 IMDS role found
Don't panic. Terraform just forgot who you are. Before you apply, you need to export your AWS credentials so Terraform can talk to your AWS account. Run these two commands and replace your_access_key and your_secret_key.
export AWS_ACCESS_KEY_ID="your_access_key_here" export AWS_SECRET_ACCESS_KEY="your_secret_key_here"
Deploy
Now that Terraform has your keys, initialize and apply your code.
terraform init terraform apply
Type yes when prompted. Wait a few seconds for AWS to spin up the instances and for the user_data script to finish installing Apache.
Now check whether your web servers are alive. The best way to test this is to check directly from your terminal.
curl http://YOUR_INSTANCE_IP
If it spits back "Hello from my AWS Spot Instance!", your Auto Scaling Group is working perfectly. If AWS reclaims one of those servers, the ASG will instantly spin up a replacement, run your script, and get you back to two instances automatically.
Clean Up (for tester)
If you are a tester and this was created for testing purposes, don't leave it running. Destroy the resources so AWS doesn't keep charging you. Run the command below to destroy the entire setup.
terraform destroy
Type yes when prompted to destroy the Terraform resources.
Conclusion
Spot instances are totally capable for production workloads if you handle them correctly. By wrapping them in an Auto Scaling Group with a mixed instances policy, you get the massive 90% cost savings of the Spot market without the panic of manual downtime. If AWS pulls the plug on a server, Terraform and AWS handle the replacement automatically. That is a big thing for your business, because your application stays online and your cloud bill stays low.