Skip to content

Spot & Preemptible Instances Guide — 80-90% Discount on Compute

DodaTech Updated 2026-06-20 8 min read

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

  1. No checkpointing for batch jobs: Without checkpoints, a spot interruption wipes hours of progress. Save state every 5-10 minutes.

  2. Using spot for stateful workloads: Databases, queues, and stateful services should never run on spot. Use on-demand or reserved for stateful services.

  3. Single instance type in fleet requests: Spot capacity varies by instance type. Always diversify across 3-5 instance types for higher availability.

  4. Ignoring the 24-hour preemptible limit on GCP: GCP preemptible VMs terminate after 24 hours. Use Spot VMs for longer-running workloads.

  5. No pod disruption budgets in K8s: Without PDBs, spot interruptions can terminate all pods simultaneously. Set maxUnavailable: 1 at minimum.

Practice Questions

  1. 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.

  2. 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.

  3. 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 happens when a spot instance is terminated?

: AWS gives a 2-minute termination notice. Azure gives 30 seconds. GCP preemptible VMs get 30 seconds. Your application should save state and exit gracefully during this window.

Which cloud provider has the best spot instance availability?

: AWS has the largest spot capacity pool with the most instance types. GCP and Azure have good availability for popular instance types. All three offer similar discount ranges.

Can I run databases on spot instances?

: Not recommended. Spot instances can be terminated at any time. Use on-demand or reserved instances for stateful workloads like databases.

How do Kubernetes and spot instances work together?

: Use spot node pools with Cluster Autoscaler or Karpenter. Set pod disruption budgets to control how many pods can be evicted at once. Use node affinity to prefer spot but fall back to on-demand.

Are spot prices stable?

: Spot prices fluctuate based on supply and demand, but they are typically stable for popular instance types. The maximum price you pay is the on-demand rate. Use max-price -1 (AWS) or max-price -1 (Azure) to never pay more than on-demand.

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