Amazon EKS — Spot Instances Strategy
Spot Instances let you use unused EC2 capacity at up to 90% discount. For fault-tolerant Kubernetes workloads — web servers, batch jobs, CI runners — Spot is the single largest cost saver.
How Spot Works
- You request Spot capacity from a pool (instance type + AZ)
- AWS provides it at the Spot price (fluctuating, but typically 60-90% off On-Demand)
- If AWS needs the capacity back, you get a 2-minute interruption notice
- The instance is terminated (your pods are evicted)
Spot-Friendly Workloads
| Workload | Spot-Friendly? | Notes |
|---|---|---|
| Stateless web servers | ✅ Yes | Multiple replicas handle interruptions |
| Batch processing | ✅ Yes | Jobs can restart |
| CI/CD runners | ✅ Yes | Builds can be retried |
| Dev/test environments | ✅ Yes | Interruptions acceptable |
| Databases | ❌ No | Use On-Demand or Reserved |
| Single-replica critical services | ❌ No | No redundancy during interruption |
| Long-running ML training | ⚠️ Maybe | Use checkpointing |
Capacity-Optimized Allocation
Use capacity-optimized allocation strategy — AWS selects pools with the most available capacity, reducing interruption rates:
eksctl create nodegroup \
--cluster my-cluster \
--name spot-workers \
--instance-types m6i.large,m6i.xlarge,m5.large,m5.xlarge,m5a.large,m5a.xlarge \
--spot \
--nodes-min 2 \
--nodes-max 20 \
--node-labels "lifecycle=spot" \
--node-taints "spot=true:NoSchedule"
Critical: Diversify across at least 6-10 instance types and all AZs. Don’t rely on a single instance type.
Mixed On-Demand + Spot Strategy
Run critical workloads on On-Demand, everything else on Spot:
# On-Demand node group (critical)
eksctl create nodegroup \
--cluster my-cluster \
--name on-demand-critical \
--instance-types m6i.xlarge \
--nodes-min 2 \
--nodes-max 5 \
--node-labels "lifecycle=on-demand,workload=critical"
# Spot node group (non-critical)
eksctl create nodegroup \
--cluster my-cluster \
--name spot-general \
--instance-types m6i.large,m6i.xlarge,m5.large,m5.xlarge,m5a.large \
--spot \
--nodes-min 2 \
--nodes-max 30 \
--node-labels "lifecycle=spot,workload=general" \
--node-taints "spot=true:NoSchedule"
Schedule workloads with tolerations and affinity:
apiVersion: apps/v1
kind: Deployment
metadata:
name: non-critical-app
spec:
replicas: 4
template:
spec:
tolerations:
- key: spot
operator: Equal
value: "true"
effect: NoSchedule
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: lifecycle
operator: In
values: ["spot"]
containers:
- name: app
image: my-app:latest
AWS Node Termination Handler
Catches Spot interruption notices and gracefully drains nodes:
helm repo add eks https://aws.github.io/eks-charts
helm install aws-node-termination-handler eks/aws-node-termination-handler \
--namespace kube-system \
--set enableSpotInterruptionDraining=true \
--set enableScheduledEventDraining=true \
--set enableRebalanceMonitoring=true \
--set enableRebalanceDraining=true
What it does:
- Monitors EC2 metadata for Spot interruption warnings (2 min notice)
- Cordons the node (no new pods scheduled)
- Drains the node (evicts pods, respecting PDBs)
- Pods reschedule on remaining nodes
Karpenter Spot Integration
Karpenter handles Spot natively and is the recommended approach:
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: spot
spec:
template:
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["spot"]
- key: node.kubernetes.io/instance-type
operator: In
values: ["m6i.large", "m6i.xlarge", "m5.large", "m5.xlarge", "m5a.large", "c6i.large", "c6i.xlarge", "r6i.large"]
- key: topology.kubernetes.io/zone
operator: In
values: ["us-east-1a", "us-east-1b", "us-east-1c"]
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: default
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 1m
Karpenter with Spot:
- Automatically replaces Spot instances when interrupted
- Tries multiple instance types and AZs simultaneously
- Falls back to On-Demand if no Spot available (if configured)
Savings Comparison
| Strategy | Monthly Compute Cost | Savings |
|---|---|---|
| All On-Demand (10 × m6i.xlarge) | $1,400 | Baseline |
| All Spot (10 × m6i.xlarge) | $420-560 | 60-70% |
| Mixed (3 OD + 7 Spot) | $714-812 | 42-49% |
| Reserved (1yr) | $910 | 35% |
| Savings Plans (1yr) | $952 | 32% |
Best Practices
- Diversify instance types — at least 6-10 types across 3 AZs
- Use
capacity-optimizedallocation — not lowest-price - Install Node Termination Handler (if not using Karpenter) — graceful drains
- Taint Spot nodes — only schedule tolerant workloads
- Always run PDBs — prevent all replicas from being evicted simultaneously
- Use Karpenter — it handles Spot more intelligently than ASG-based approaches