Spot & Preemptible Instances Guide — 80-90% Discount on Compute
In this tutorial, you'll learn about Spot & Preemptible Instances Guide. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
Spot and preemptible instances offer 80-90% discounts on cloud compute across AWS, Azure, and GCP — ideal for batch processing, CI/CD pipelines, stateless web workers, and any fault-tolerant workload that can handle interruption.
What You'll Learn
You'll deploy AWS Spot Instances with Spot Fleet, configure Azure Spot VMs with eviction policies, set up GCP preemptible and Spot VMs with graceful shutdown, design checkpointing for long-running batch jobs, and run spot node pools in Kubernetes.
Why It Matters
On-demand compute is the most expensive option. For workloads that can tolerate interruption, spot instances reduce compute costs by 80-90%. DodaTech runs all DodaZIP build agents and Durga Antivirus Pro's malware analysis sandbox on spot instances, saving $14k/month compared to on-demand pricing.
flowchart TD
A[Workload Type] --> B{Fault-Tolerant?}
B -->|Yes| C[Spot / Preemptible]
B -->|No| D[On-Demand / Reserved]
C --> E{Cloud Provider}
E -->|AWS| F[Spot Fleet / Mixed ASG]
E -->|Azure| G[Spot VMs + Eviction Policy]
E -->|GCP| H[Preemptible / Spot VMs]
F --> I[Checkpointing]
G --> I
H --> I
I --> J[80-90% Savings]
style J fill:#22c55e,color:#fff
1. AWS Spot Instances
AWS Spot Instances use spare EC2 capacity at up to 90% discount. Pricing fluctuates based on supply and demand.
# Request Spot Instances with a launch specification
aws ec2 request-spot-instances \
--spot-price "0.05" \
--instance-count 5 \
--type "one-time" \
--launch-specification '{
"ImageId": "ami-0c55b159cbfafe1f0",
"InstanceType": "m5.large",
"Placement": {"AvailabilityZoneGroup": "us-east-1a"},
"SecurityGroups": [{"GroupId": "sg-12345"}]
}'
# Use Spot Fleet for diversification
aws ec2 request-spot-fleet \
--spot-fleet-request-config '{
"AllocationStrategy": "lowestPrice",
"TargetCapacity": 20,
"IamFleetRole": "arn:aws:iam::123456789012:role/spot-fleet-role",
"LaunchSpecifications": [
{"InstanceType": "m5.large", "ImageId": "ami-0c55b159cbfafe1f0", "WeightedCapacity": 1},
{"InstanceType": "m5.xlarge", "ImageId": "ami-0c55b159cbfafe1f0", "WeightedCapacity": 2},
{"InstanceType": "c5.large", "ImageId": "ami-0c55b159cbfafe1f0", "WeightedCapacity": 1},
{"InstanceType": "r5.large", "ImageId": "ami-0c55b159cbfafe1f0", "WeightedCapacity": 1}
]
}'
# Monitor spot instance interruptions
aws ec2 describe-spot-instance-requests \
--query "SpotInstanceRequests[?Status.Code=='instance-terminated-by-price']"
Expected output from Spot Fleet request:
{
"SpotFleetRequestId": "sfr-abc123",
"SpotFleetRequestState": "active",
"TargetCapacity": 20,
"FulfilledCapacity": 20.0
}
Interruption Handling with Lambda
import boto3
import json
ec2 = boto3.client('ec2')
sns = boto3.client('sns')
def lambda_handler(event, context):
"""Handle Spot Instance interruption notice."""
# Parse the interruption notice
detail = event['detail']
instance_id = detail['instance-id']
action = detail['instance-action'] # 'terminate', 'stop', 'hibernate'
print(f"Interruption notice for {instance_id}: {action}")
# Save checkpoint before termination
instance = ec2.describe_instances(InstanceIds=[instance_id])
tags = {t['Key']: t['Value'] for t in instance['Reservations'][0]['Instances'][0].get('Tags', [])}
checkpoint_data = {
'instance_id': instance_id,
'action': action,
'tags': tags,
'interruption_time': event['time']
}
# Publish to checkpoint queue
sqs = boto3.client('sqs')
sqs.send_message(
QueueUrl='https://sqs.us-east-1.amazonaws.com/123456789012/spot-checkpoints',
MessageBody=json.dumps(checkpoint_data)
)
# Notify ops
sns.publish(
TopicArn='arn:aws:sns:us-east-1:123456789012:ops-alerts',
Subject=f'Spot interruption: {instance_id}',
Message=json.dumps(checkpoint_data)
)
return {'instance_id': instance_id, 'action': action, 'checkpoint_saved': True}
2. Azure Spot VMs
Azure Spot VMs offer up to 90% discount but can be evicted when Azure needs capacity back.
# Create a Spot VM with Deallocate eviction policy
az vm create \
--resource-group batch-rg \
--name batch-vm-01 \
--image UbuntuLTS \
--size Standard_D4s_v3 \
--priority Spot \
--eviction-policy Deallocate \
--max-price -1 # Pay up to on-demand price
# Create a Spot VM Scale Set
az vmss create \
--resource-group batch-rg \
--name spot-batch-vmss \
--image UbuntuLTS \
--instance-count 0 \
--vm-sku Standard_D4s_v3 \
--priority Spot \
--eviction-policy Deallocate \
--single-placement-group false \
--max-price 0.05
# Set auto-scaling for Spot VMSS
az monitor autoscale create \
--resource-group batch-rg \
--name spot-autoscale \
--resource spot-batch-vmss \
--resource-type Microsoft.Compute/virtualMachineScaleSets \
--min-count 0 \
--max-count 20 \
--count 0
az monitor autoscale rule create \
--resource-group batch-rg \
--autoscale-name spot-autoscale \
--condition "Percentage CPU > 70 avg 5m" \
--scale out 3
Azure Spot pricing:
| VM Size | On-Demand | Spot (avg) | Savings |
|---|---|---|---|
| Standard_D4s_v3 | $0.19/hr | $0.03/hr | 84% |
| Standard_E8s_v3 | $0.45/hr | $0.07/hr | 84% |
| Standard_NC6 | $1.80/hr | $0.18/hr | 90% |
3. GCP Preemptible and Spot VMs
GCP offers preemptible VMs (max 24 hours, 91% discount) and Spot VMs (no time limit, similar discount).
# Create a preemptible VM (max 24 hours)
gcloud compute instances create batch-worker \
--zone us-central1-a \
--machine-type n2-standard-8 \
--preemptible \
--maintenance-policy TERMINATE
# Create a Spot VM (no 24-hour limit)
gcloud compute instances create spot-worker \
--zone us-central1-a \
--machine-type n2-standard-8 \
--provisioning-model SPOT \
--instance-termination-action STOP
# Create a preemptible instance template for MIG
gcloud compute instance-templates create preemptible-worker \
--machine-type n2-standard-4 \
--preemptible \
--image-family ubuntu-2204-lts \
--image-project ubuntu-os-cloud \
--metadata shutdown-script='#!/bin/bash
echo "Saving checkpoint..."
gsutil cp /var/state/current.job gs://dodatech-checkpoints/$(hostname).job
echo "Checkpoint complete."'
# Create managed instance group with preemptible template
gcloud compute instance-groups managed create preemptible-mig \
--zone us-central1-a \
--template preemptible-worker \
--target-size 0 \
--max-instances 50
Checkpointing Strategy
# checkpoint_manager.py — resume interrupted spot workloads
import json
import os
import time
import signal
import subprocess
class CheckpointManager:
"""Save and restore job state for spot instance interruptions."""
def __init__(self, checkpoint_dir: str = "/var/checkpoints"):
self.checkpoint_dir = checkpoint_dir
os.makedirs(checkpoint_dir, exist_ok=True)
signal.signal(signal.SIGTERM, self._handle_shutdown)
def save_checkpoint(self, job_id: str, state: dict):
"""Save job progress to disk."""
path = os.path.join(self.checkpoint_dir, f"{job_id}.json")
state['timestamp'] = time.time()
with open(path, 'w') as f:
json.dump(state, f)
print(f"Checkpoint saved: {job_id} at {state['progress']:.1f}%")
def load_checkpoint(self, job_id: str) -> dict:
"""Restore job progress from last checkpoint."""
path = os.path.join(self.checkpoint_dir, f"{job_id}.json")
if os.path.exists(path):
with open(path) as f:
return json.load(f)
return {'progress': 0, 'items_processed': []}
def run_with_checkpoints(self, items: list, job_id: str, process_func):
"""Process items with periodic checkpointing."""
state = self.load_checkpoint(job_id)
start_index = len(state.get('items_processed', []))
for i, item in enumerate(items[start_index:], start=start_index):
process_func(item)
state['items_processed'].append(item.id)
state['progress'] = ((i + 1) / len(items)) * 100
# Save checkpoint every 50 items
if (i + 1) % 50 == 0:
self.save_checkpoint(job_id, state)
self.save_checkpoint(job_id, state)
print(f"Job {job_id} complete. Processed {len(items)} items.")
def _handle_shutdown(self, signum, frame):
"""Save checkpoint on SIGTERM (spot interruption)."""
print("Interruption detected. Saving final checkpoint...")
# The main loop calls save_checkpoint, but ensure last state is written
exit(0)
# Usage
manager = CheckpointManager()
def process(item):
time.sleep(0.1) # Simulate work
print(f"Processed item {item}")
items = list(range(1000))
manager.run_with_checkpoints(items, "data-export-job", Process)
4. Spot Instances in Kubernetes
# EKS: Create a spot-managed node group
eksctl create nodegroup \
--cluster dodatech-eks \
--name spot-workers \
--node-type m5.large \
--nodes-min 0 \
--nodes-max 20 \
--spot \
--spot-instance-types m5.large,c5.large,r5.large \
--asg-target-group-arn "arn:aws:elasticloadbalancing:..."
# AKS: Create a spot node pool
az aks nodepool add \
--resource-group dodatech-rg \
--cluster-name dodatech-aks \
--name spotpool \
--node-vm-size Standard_D4s_v3 \
--priority Spot \
--eviction-policy Delete \
--spot-max-price -1 \
--enable-cluster-autoscaler \
--min-count 0 \
--max-count 10
Common Mistakes
No checkpointing for batch jobs: Without checkpoints, a spot interruption wipes hours of progress. Save state every 5-10 minutes.
Using spot for stateful workloads: Databases, queues, and stateful services should never run on spot. Use on-demand or reserved for stateful services.
Single instance type in fleet requests: Spot capacity varies by instance type. Always diversify across 3-5 instance types for higher availability.
Ignoring the 24-hour preemptible limit on GCP: GCP preemptible VMs terminate after 24 hours. Use Spot VMs for longer-running workloads.
No pod disruption budgets in K8s: Without PDBs, spot interruptions can terminate all pods simultaneously. Set
maxUnavailable: 1at minimum.
Practice Questions
What is the difference between GCP Preemptible VMs and Spot VMs? Answer: Preemptible VMs have a 24-hour maximum runtime and 91% discount. Spot VMs have no time limit, similar discount, but can still be terminated when capacity is needed.
How do you handle spot instance interruptions gracefully? Answer: Use checkpointing to save job state every 5-10 minutes, listen for the termination notice (AWS: 2 min, Azure: 30s, GCP: 30s), and drain connections before shutdown.
Why diversify instance types in Spot Fleet requests? Answer: Different instance types have different spot capacity pools. If one pool dries up, the fleet falls back to others. Use 3-5 instance types per request.
Challenge
Design a spot-based batch processing pipeline for 10,000 video encoding jobs per day: use AWS Spot Fleet with 5 instance types across 3 AZs, implement checkpointing at 5-minute intervals, store checkpoints in S3, set up SNS notifications for interruptions, and build a Lambda function that re-queues interrupted jobs. Calculate the monthly savings compared to on-demand.
FAQ
What's Next
| Topic | Description |
|---|---|
| {{< card link="../right-sizing-strategies" title="Right-Sizing Strategies" icon="chart-bar" >}} | Right-size on-demand and spot instances |
| {{< card link="../kubernetes-cost-optimization" title="Kubernetes Cost Optimization" icon="server" >}} | Spot node pools in K8s |
Related topics: Cloud Cost Optimization, Cloud Computing, AWS
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro