Engineering Cost-Efficient Infrastructure: Provisioning AWS EC2 Spot Instances with Terraform

Cloud cost optimization is a critical pillar of modern DevOps and platform engineering. For organizations managing non-production environments, batch processing, or stateless services, the financial burden of On-Demand pricing can be prohibitive. AWS EC2 Spot Instances offer a powerful solution by allowing users to purchase spare AWS compute capacity at significant discounts—up to 90% off the On-Demand price. However, the trade-off for this cost reduction is the potential for interruption, as AWS can reclaim this capacity with a two-minute warning when the compute is needed elsewhere.

To manage this volatility at scale, Infrastructure as Code (IaC) is essential. Terraform, developed by HashiCorp, provides the necessary framework to reproducibly create, modify, and destroy these instances. By defining spot instance configurations in code, engineers can automate the lifecycle of their infrastructure, ensuring that environments are consistent and easily recoverable after an AWS reclamation event.

Understanding AWS EC2 Spot Instances

AWS EC2 (Elastic Compute Cloud) represents virtualized servers within Amazon's global data centers. While On-Demand instances provide a guaranteed level of availability for a fixed hourly rate, Spot Instances leverage the unused capacity of the AWS fleet. This makes them ideal for workloads that are fault-tolerant or can be paused and resumed without significant data loss.

Ideal Workload Profiles for Spot Instances

Not every application is suitable for a Spot environment. Because AWS can reclaim the instance at any time, the architecture must be designed to handle interruptions.

  • Batch Processing: Tasks that can be broken down into smaller chunks and resumed if a node fails.
  • CI/CD Runners: Build and test agents that can simply restart their jobs on a new instance.
  • Stateless Web Servers: Servers sitting behind a load balancer where the loss of one instance does not drop the entire service.
  • Data Processing Pipelines: Distributed computing frameworks that inherently handle node failure.

The Trade-off Matrix: On-Demand vs. Spot

Feature On-Demand Instances Spot Instances
Cost Standard hourly rate Up to 90% discount
Availability Guaranteed until terminated by user Can be reclaimed by AWS
Warning Period N/A 2-minute interruption warning
Best Use Case Production, critical stateful apps Dev/Test, batch, stateless apps
Pricing Model Fixed Market-based / Bidding

Prerequisites for Terraform Implementation

Before deploying Spot instances via Terraform, several environmental configurations must be in place to ensure the IaC tool can communicate with the AWS API and authenticate requests.

AWS Account and CLI Setup

A valid AWS account is required. To enable Terraform to manage resources, the AWS Command Line Interface (CLI) must be installed and configured. Terraform interacts with the AWS CLI credentials stored locally on the machine (typically in ~/.aws/credentials) to perform authentication.

IAM Permissions

The identity (IAM User or Role) used by Terraform must have the necessary rights to create EC2 instances, request spot capacity, and manage networking components like Security Groups and Subnets. These permissions should be configured through the AWS IAM console following the principle of least privilege.

Terraform Installation

Terraform must be installed on the local workstation or CI/CD runner. As an open-source tool, it allows for the definition of the entire infrastructure in .tf files. For those preferring alternatives, similar projects like OpenTofu also support these configurations.

Provisioning Strategies in Terraform

Terraform provides multiple ways to deploy Spot instances depending on the desired level of complexity, scale, and persistence.

Method 1: Simple Spot Instance Request (aws_instance)

The most straightforward method for deploying a single Spot instance is adding the instance_market_options block to a standard aws_instance resource. This approach is best for simple testing purposes or single-node workers.

```hcl

Create a single spot instance

resource "awsinstance" "spotworker" {
ami = data.awsami.amazonlinux.id
instancetype = "t3.large"
subnet
id = awssubnet.private.id
vpc
securitygroupids = [awssecuritygroup.worker.id]

# Request this instance as a spot instance
instancemarketoptions {
markettype = "spot"
spot
options {
# Maximum price you're willing to pay per hour
max_price = "0.05"

  # "one-time" means the request is not re-submitted if interrupted
  # "persistent" means AWS will try to re-launch after interruption
  spot_instance_type = "one-time"
}

}

user_data = <<-EOF
#!/bin/bash
echo "Starting spot worker..."
EOF

tags = {
Name = "spot-worker"
}
}
```

In this configuration, the max_price attribute is critical. If this value is omitted, AWS defaults the maximum price to the current On-Demand price for that instance type. The spot_instance_type defines whether the request is one-time or persistent.

Method 2: Using awsspotinstance_request

For more granular control over the spot request itself—separate from the instance lifecycle—the aws_spot_instance_request resource is used. This is particularly useful when managing how the instance behaves during interruptions.

hcl resource "aws_spot_instance_request" "my-spot" { spot_price = "$.$$$" ami = "ami-xxxxx" wait_for_fulfillment = true spot_type = "persistent" instance_interruption_behaviour = "stop" instance_type = "my.metal" key_name = aws_key_pair.my-key.key_name }

Key parameters in this resource include:
- wait_for_fulfillment: If set to true, Terraform will wait for the spot instance to be fulfilled before marking the resource as created.
- instance_interruption_behaviour: This can be set to stop or terminate, defining what happens to the instance when AWS reclaims the capacity.
- spot_type: Setting this to persistent ensures AWS attempts to launch a new instance once capacity becomes available again.

Method 3: Scaling with Auto Scaling Groups (ASG)

For production-grade non-production environments, single instances are rarely sufficient. Using an aws_autoscaling_group in conjunction with an aws_launch_template allows for a hybrid approach to capacity.

To implement this, a mixed_instances_policy block must be defined within the ASG resource. This block consists of:

  1. instances_distribution: This defines the ratio between On-Demand and Spot instances. While some users may want a small percentage of On-Demand instances to handle critical workflows, many cost-optimization strategies set this to 100% Spot.
  2. launch_template: Specifies the AMI, instance type, and other configuration details.
  3. spotallocationstrategy: This tells AWS how to select the spot instances (e.g., based on price or instance type availability).

Deployment Workflow and Lifecycle Management

The operational cycle of managing Spot instances with Terraform follows a standardized IaC workflow.

Initializing and Applying

Once the configuration files are written, the following sequence is executed:

  1. terraform init: Initializes the working directory and downloads the necessary AWS providers.
  2. terraform apply: Executes the plan to create the resources. If using a variable file for environment-specific data (such as AMI IDs), the command is modified to terraform apply -var-file="example.tfvars".

During the apply phase, Terraform communicates with AWS to request the Spot capacity. If the request is successful, a terraform.tfstate file is generated locally or in a remote backend. This state file is vital as it contains the mapping of Terraform resources to real-world AWS IDs, including the public IP address of the created instance.

Connection and Validation

Once the instance is live, it can be accessed via SSH. A common challenge is the timing of SSH connectivity; if the instance is still booting, the connection will fail. Some advanced Terraform configurations include a "sleep" mechanism or local-exec provisions to modify the known_hosts file, preventing the user from being prompted to accept a new SSH key manually.

Example connection command:
ssh root@ip_address

Destruction

To avoid unnecessary costs once testing is complete, the infrastructure should be torn down.
terraform destroy -var-file="example.tfvars"

This command removes all requested Spot instances and associated resources, ensuring the AWS bill remains optimized.

Advanced Networking Considerations

A common hurdle for engineers is deploying Spot instances within custom Virtual Private Clouds (VPCs) rather than the default AWS VPC.

Custom VPC and Private Subnets

While simple aws_spot_instance_request resources may default to the default VPC if not specified, professional deployments require placing instances in private subnets for security. To achieve this, the subnet_id must be explicitly passed to the resource, referencing a aws_subnet resource created within a custom aws_vpc.

Limitations of Single Private Spot Instances

It has been noted that deploying single private spot instances using specific resource types can sometimes be unsupported or limited. In these scenarios, the recommended expert path is to leverage Auto Scaling Groups (ASGs) or a Spot Fleet. ASGs are particularly effective because they automatically handle the replacement of interrupted instances within the specified subnet and security group parameters.

Technical Summary of Configuration Options

Parameter Resource Description Recommended Value
max_price aws_instance Highest price paid per hour Based on market history
spot_type aws_spot_instance_request Request lifecycle persistent for resilience
interruption_behaviour aws_spot_instance_request Action on reclamation stop to preserve disk
mixed_instances_policy aws_autoscaling_group Blend of Spot/On-Demand 100% Spot for max savings

Conclusion

The integration of AWS EC2 Spot Instances with Terraform transforms the way engineers handle volatile compute capacity. By leveraging the cost-saving potential of Spot instances—which can reduce expenses by up to 90%—organizations can significantly scale their testing and batch processing capabilities without a linear increase in budget.

The technical strength of this approach lies in the variety of implementation paths. For simple, ephemeral tasks, adding instance_market_options to a standard aws_instance provides a quick entry point. For more robust, self-healing systems, the combination of aws_launch_template and aws_autoscaling_group with a mixed_instances_policy ensures that the infrastructure can withstand the inherent instability of Spot capacity.

Ultimately, the "Infrastructure as Code" mantra allows for the total automation of these processes. From the initial terraform init to the final terraform destroy, every aspect of the Spot instance lifecycle can be version-controlled and reproduced. While setting up EC2 instances may be more complex than using simpler providers like Digital Ocean, the granular control provided by Terraform and AWS enables the creation of highly optimized, professional-grade cloud environments.

Sources

  1. https://www.tderflinger.com/ec2-spot-with-terraform
  2. https://www.teracloud.io/single-post/optimize-your-costs-with-aws-spot-instances-and-terraform-in-just-a-few-steps
  3. https://oneuptime.com/blog/post/2026-02-23-create-ec2-spot-instances-with-terraform/view
  4. https://github.com/jftuga/terraformec2spot_instance
  5. https://discuss.hashicorp.com/t/how-to-deploy-spot-instances-in-your-custom-vpc/20802

Related Posts