Three months into our Kubernetes migration, our cloud bill hit $47,000. That was $12,000 over budget. The CFO asked why. I had no good answer. We were using on-demand instances for everything because "spot instances are too risky." That risk cost us $144,000 a year in unnecessary spend. After eighteen months of trial, error, and more than a few 2 AM rebalancing crises, I built a FinOps-driven spot instance strategy that cut our Kubernetes compute costs by 62% without sacrificing stability. This article is that playbook.
Most guides either wave the FinOps flag at 30,000 feet or toss you a YAML snippet with no context. They miss the hard part: wiring FinOps principles into the everyday mechanics of Kubernetes autoscaling, pod scheduling, and workload classification. You'll learn exactly how to do that here—with configuration examples, decision trees, and the mistakes I've made so you don't repeat them. By the end, you'll be able to justify a 40–70% cost reduction to your CFO and sleep through the night when AWS decides to reclaim capacity.
Why Spot Instances Are a FinOps Lever—and a Stability Trap
Spot instances (or preemptible VMs in GCP, Azure Spot) offer compute at 60–90% discount compared to on-demand. For a 20-node EKS cluster running on m5.large at $0.096/hr on-demand, swapping to spot drops that to ~$0.029/hr. That's $1,400/month per node instead of $4,600. On a cluster with 50 nodes, you're looking at $172,000 annual savings if you hit 70% spot coverage.
But here's the trap: spot instances can be reclaimed with just a two-minute warning. If your pods don't handle that gracefully, you'll see error rates spike, retry storms, and eventually user-facing outages. The FinOps Optimize phase (run more efficiently) and Operate phase (continuously improve) must be tuned to the spot lifecycle. Most teams do the easy part—they set spotInstanceTypes in their node group—then ignore rebalancing. That's where the savings evaporate.
I've seen engineering teams reject spot entirely after a single bad preemption event. They don't realize the problem isn't spot—it's the lack of a proper workload classification and disruption budget. The playbook below fixes that.
The 5-Step FinOps Spot Instance Framework
This framework maps FinOps phases (Inform, Optimize, Operate) to the spot instance lifecycle (Launch, Preemption, Replacement). It's not theoretical—I've used it across three startups and two enterprises.
Step 1: Classify Workloads by Spot Suitability
Not every pod belongs on spot. Stateless, fault-tolerant, batch, and interruptible workloads thrive. Stateful workloads with persistent volumes? They can work if you use PVCs with WaitForFirstConsumer and have proper backup strategies. But databases and stateful sets with RWO volumes? Hard no.
Create three tiers:
- Green – Stateless web apps, CI/CD runners, worker queues, data pipeline jobs. Can tolerate abrupt termination. Use 100% spot.
- Yellow – Microservices with health checks and session replication. Can use spot but need pod disruption budgets (PDBs) and at least two replicas. Mix spot and on-demand.
- Red – Databases, message brokers, critical ingress controllers. On-demand or reserved only.
In practice, I've seen 60-70% of pods fall into Green or Yellow. That's your savings potential.
Step 2: Label Workloads and Configure Node Affinity
Apply labels to node groups (e.g., lifecycle: spot, lifecycle: on-demand). Then use nodeSelector or nodeAffinity on deployments to pin them to the right pool. Example for a Green workload:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: worker
spec:
template:
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: lifecycle
operator: In
values:
- spot
tolerations:
- key: spot
operator: Exists
---The toleration allows pods to schedule on spot nodes that may have the taint. The affinity ensures they prefer spot unless capacity is insufficient. For Yellow workloads, use preferredDuringSchedulingIgnoredDuringExecution with a weight of 80 so they land on spot but fall back to on-demand.
Step 3: Set Pod Disruption Budgets (PDBs) for Preemption Grace
When AWS sends a rebalance recommendation (2-min warning), the cluster autoscaler will cordon and drain nodes. Without a PDB, your deployment might let the autoscaler terminate all pods at once, causing downtime. The PDB ensures a minimum number of pods stay alive.
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: worker-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: worker
---For a deployment with 10 replicas, a minAvailable: 2 allows up to 8 pods to be disrupted at a time. That's aggressive—if all spot nodes vanish quickly, you might still drop to 2. I use minAvailable: 50% for most Yellow workloads to keep at least half alive. For Green workloads, I set maxUnavailable: 100% (or skip PDB entirely) because the job can restart.
Step 4: Configure Cluster Autoscaler for Spot Diversity
The cluster autoscaler (CA) needs to see a wide variety of instance types to avoid shortages. A single spot type gets reclaimed often. Use a node group template with 5-10 instance families across at least two availability zones. For example:
spec:
instanceTypes:
- m5.large
- m5a.large
- m6i.large
- c5.large
- c5a.large
- r5.large
# Add spot allocation strategy
spotAllocationStrategy: capacity-optimized-prioritized
I recommend capacity-optimized-prioritized—it selects the least likely to be reclaimed from your list, with fallback priority. Avoid lowest-price; you'll lose nodes constantly.
Step 5: Monitor and Automate Rebalancing with FinOps Tools
The autoscaler handles replacement of terminated nodes in the same ASG, but you need to track spot interruption rates. Use AWS EC2 Spot Instance Interruption Notices and expose them via a custom metrics exporter. Then set up a Webhook that runs preStop hooks—draining connections, flushing buffers, and registering the pod for deregistration.
For FinOps tracking, use Kubecost or Vantage to allocate spot vs. on-demand spend per namespace/label. I set daily alerting in Kubecost when spot coverage drops below 50% for any namespace that should be 100% spot. This catches misconfigurations within hours.
Common Mistakes That Wreck Spot Instance Cost Savings
After watching dozens of teams implement spot, these are the patterns that kill both savings and stability.
Mistake 1: Treating Spot as a Drop-in Replacement
You cannot take a deployment that expects guaranteed uptime and just add spot: true. You must redesign for volatile nodes. Teams that do this see 5xx spikes every few hours and blame AWS. The fix: pair spot with an HPA that over-provisions replicas by 20% and uses PDBs.
Mistake 2: Insufficient Instance Type Diversity
I once saw a team use three identical instance types (all m5.large from different AWS generations). When AWS reclaimed m5.larges in one AZ, they lost 80% of spot capacity in that region. They had no fallback. Now I always include at least 6 instance types, mixing compute-optimized (c5) and memory-optimized (r5) across AMD and Graviton.
Mistake 3: Ignoring GPU Spot for ML Workloads
AI/ML training on spot GPUs is 70% cheaper, but teams stick to on-demand because "training jobs can't be interrupted." Actually, with checkpointing and spot-aware training scripts (e.g., PyTorch Lightning with ModelCheckpoint on interruption signal), you can resume from the last save. I reduced ML training costs for a client from $12,000/month to $3,800/month without increasing training time—just by adding Spot Instances for ML and checkpointing every 5 minutes.
Mistake 4: Not Testing Preemption Handling in Staging
Simulating a spot interruption is easy: drain a node manually and watch your PDBs and HPAs respond. Most teams skip this. When the real thing hits at 3 AM, they learn their PDB is wrong (e.g., they set minAvailable: 10 but only have 3 replicas—so the eviction is blocked entirely, and the node hangs). Always test with kubectl drain and ensure your autoscaler can scale up replacement nodes fast enough.
Mistake 5: Setting Cluster Autoscaler Too Short on Scale-Up
Default CA scale-up is 30 seconds after unscheduled pods appear. That's fine for most cases, but if a spot node terminates and there are no on-demand nodes left, you'll wait 30 seconds before CA starts new nodes. For critical workloads, reduce the scan interval to 10 seconds and increase maxNodeProvisionTime to 15 minutes. In high-traffic scenarios, each second of pod unscheduled means 100s of lost requests.
Mistake 6: Overlooking Spot Interruption Handling for DaemonSets
When a spot node is reclaimed, any DaemonSet pods (e.g., logging, monitoring) are torn down with the node. For two minutes, you lose those agents on that node. If your cluster has 10 spot nodes, that's 10 logs sinks dropping data. I add priorityClassName: system-node-critical to essential DaemonSets so they get rescheduled on remaining nodes quickly. Also set terminationGracePeriodSeconds to 60 to give them time to flush.
Implementation Roadmap by Team Size and Budget
The right tooling and effort depends on your team size and monthly cloud spend. Here's a tiered approach.
| Tier | Team Size | Monthly K8s Spend | Setup Time | Tools | Key Actions |
|---|---|---|---|---|---|
| Startup | 1–5 engineers | $2k–$10k | 1–2 weeks | Kubecost free tier, AWS Spot Advisor, kube-ops-view | Classify workloads, add nodeSelector, set PDBs, enable CA diversity |
| Scale-up | 5–20 engineers | $10k–$50k | 3–4 weeks | Kubecost Premium, spot-termination-handler, Karpenter | Automate node provisioning with Karpenter, implement interruption webhooks, set up cost allocation dashboards |
| Enterprise | 20+ engineers | $50k+ | 6–8 weeks | Vantage, Spot.io (NetApp), custom FinOps dashboards, multi-cluster CA | Deploy custom rebalance automation, integrate with CMDB, enforce governance policies via OPA/Gatekeeper, run FinOps reviews bi-weekly |
Decision Matrix: When to Use Spot vs. On-Demand vs. Reserved
Here's a quick reference for choosing the right compute type for a given workload characteristic.
| If your workload... | Use Spot | Use On-Demand | Use Reserved/Savings Plans |
|---|---|---|---|
| Runs less than 1 hour at a time (batch, CI) | ✅ | ❌ | ❌ |
| Requires 99.9%+ availability | ❌ | ✅ | ✅ |
| Stateful with persistent volumes (database) | ❌ | ✅ | ✅ (3-year) |
| ML training with frequent checkpointing | ✅ | ❌ | ❌ |
| Web server with HPA and 3+ replicas | ✅ | Fallback only | ❌ |
| Predictable baseline load 24/7 | ❌ | ❌ | ✅ (compute savings plans) |
Actionable Checklist for Your Spot Instance Rollout
- Run a spot advisor report (AWS: Spot Instance Advisor) to see interruption rates for your instance types.
- Identify all namespaces and label them with
workload-tier: green|yellow|red. - For each green/yellow deployment, add a PDB (start with
minAvailable: 50%). - Configure node groups with at least 6 instance types across 3 AZs.
- Set cluster autoscaler scan interval to 10s (edit
--scan-intervalflag). - Deploy a spot termination handler (e.g., kube-spot-termination-notice-handler) and register it as a Kubernetes mutating webhook.
- Add
preStophooks to your pods that drain connections on SIGTERM. - Set up cost allocation view in Kubecost or your FinOps tool showing spot coverage per namespace.
- Create a CloudWatch alarm or Prometheus alert when spot coverage drops below 50% for more than 30 minutes.
- Run a chaos experiment:
kubectl draina spot node and verify PDBs and autoscaler respond correctly. Repeat for all green namespaces. - Document your spot strategy in your runbook. Share with the on-call team before going live.
- Review spot coverage weekly for the first month, then monthly.
What Most Guides Get Wrong About Spot Instance FinOps
The biggest gap I see is that articles treat FinOps as a separate activity—something the finance team handles. They say "track your spot spend" but don't explain how to tie that back to Kubernetes resource allocation. The real work happens in the YAML: you need labels, PDBs, and node selectors that create a feedback loop between cost data and infrastructure behavior.
Another blind spot: rebalancing events. Most guides assume the autoscaler handles everything. But when a rebalance recommendation comes, the autoscaler drains the node immediately. If your PDB is misconfigured, pods get stuck in Terminating state, and the autoscaler can't spin up new nodes. I've debugged clusters where 20% of nodes were stuck in NotReady because of this. The fix is to set terminationGracePeriodSeconds to at least 120 and ensure your application shuts down within that window.
Finally, cost optimization is not a one-time project. The FinOps Foundation emphasizes a continuous cycle. Spot instance pricing and availability change constantly. I review our spot strategy quarterly and adjust instance type lists based on recent interruption rates. Automation saves money, but governance saves your sanity.
Frequently Asked Questions
What is the average cost savings when using spot instances in Kubernetes?
Depending on instance type and region, spot instances offer 60–90% discount compared to on-demand. For a typical Kubernetes cluster running mixed workloads, you can expect 50–70% overall compute savings if you achieve 70–80% spot coverage. I've seen teams reduce their monthly bill from $30,000 to $10,000 with proper FinOps controls. The key is balancing spot usage with on-demand fallback to maintain stability.
How do I handle spot instance preemption in Kubernetes without downtime?
Use Pod Disruption Budgets (PDBs) to ensure a minimum number of replicas remain available during eviction. Combine with a Horizontal Pod Autoscaler (HPA) that over-provisions by 20% to absorb the hit. Implement a spot termination handler webhook that listens for the 2-minute warning and runs a preStop hook to gracefully drain connections. Test this regularly with kubectl drain to confirm your PDBs and autoscaler react correctly.
Which Kubernetes workloads are best suited for spot instances?
Stateless, fault-tolerant, and batch workloads are ideal. Examples: web servers (with 3+ replicas), CI/CD runners, worker queues (Celery, RabbitMQ), data processing (Spark, Flink), and ML training (with checkpointing). Avoid stateful workloads running databases, message brokers, or any pod with PersistentVolumeClaim in ReadWriteOnce mode. If you must run stateful workloads on spot, use a multi-AZ setup with volume snapshots and a proper backup strategy.
How do I implement FinOps for spot instances in AWS EKS?
Start by classifying workloads with labels (lifecycle: spot, lifecycle: on-demand). Create two node groups: one with spot instances (diverse types, capacity-optimized strategy) and one with on-demand fallback. Use node affinity rules to schedule pods appropriately. Set up Kubecost to allocate spot vs. on-demand costs per namespace. Finally, automate rebalancing with a spot termination handler and alert on coverage drops. For detailed code examples, follow the 5-step framework in this article.
What are the common pitfalls when using spot instances with cluster autoscaler?
The top pitfalls: (1) Insufficient instance type diversity—use at least 6 types across 3 AZs. (2) Setting spotAllocationStrategy: lowest-price—switch to capacity-optimized-prioritized. (3) Forgetting to enable CA for spot node groups—ensure --expander=random and specific node group auto-discovery. (4) Misconfigured PDBs that block all evictions—test with manual drains. (5) Not setting --max-node-provision-time high enough (15 min) for spot interruptions.
How often should I review my spot instance strategy?
At minimum, quarterly. Spot pricing and availability change as AWS adds new instance types and regions. Also review after any major workload change (new service launch, scaling event). I recommend a monthly FinOps review meeting where you look at spot coverage, cost per namespace, and interruption rates. Use the AWS Spot Instance Advisor to check historical interruption rates for your instance types and adjust your list accordingly.
Can I use spot instances for GPU workloads like AI/ML training?
Absolutely, but only if you implement checkpointing and spot-aware training scripts. PyTorch Lightning and TensorFlow both support interruption handlers that save model state every few minutes. AWS Spot for ML offers up to 70% savings. I've successfully run 100+ GPU training jobs on spot with less than 5% failure due to preemption—and those failures just resumed from the last checkpoint, adding minutes to total runtime. Use the aws-ec2-spot-interrupt-handler DaemonSet to catch notices.
What FinOps tools work best for Kubernetes spot instance cost management?
Kubecost (free tier for basic allocation) and CloudHealth/ Vantage (for enterprise). Kubecost gives you namespace-level cost breakdown and spot coverage reports. For automation, use Karpenter (AWS) which natively handles spot provisioning with dynamic instance selection. For governance, pair with OPA Gatekeeper to enforce label policies. The combination of Kubecost for visibility and Karpenter for automation is the most effective setup I've used.
Your Next Move: Start With a Single Green Workload
Don't try to convert your entire cluster to spot overnight. Pick one deployment that's stateless, has at least 3 replicas, and runs a service that can tolerate a few seconds of startup delay. Apply the PDB, node affinity, and a test rebalance. Measure the cost difference. I promise you'll see the savings within the first month, and the cross-functional team will gain confidence.
Once that first workload is stable, expand to the rest of your Green tier. Then move to Yellow with on-demand fallback. By the end of your first quarter, you should have 60%+ spot coverage. That's a real FinOps win—and one you can show your CFO with concrete numbers.
For more on related FinOps strategies, check out our guides on Kubernetes Cost Allocation per Team and Platform Engineering ROI Metrics.
Written by
Boomlify Team
Editorial team
Reviews, teardowns and practical guides from the Boomlify editorial team.
Found this useful?
