Kubernetes bills creep up quietly. Nodes get over-provisioned "just to be safe," forgotten workloads keep running, and nobody owns the cost. Here are five changes we apply on almost every AWS engagement — each is low-risk and measurable.
1. Right-size requests and limits
Most pods request far more CPU and memory than they use. Pull two weeks of usage from Prometheus and set requests close to the p95, not the peak.
# Top pods by CPU vs. their requests
kubectl top pods -A --sort-by=cpu
Rule of thumb: if cluster CPU requests are above 80% but actual usage is below 30%, you're paying for headroom you don't need.
2. Turn on the Cluster Autoscaler (or Karpenter)
Static node groups mean you pay for the busiest hour, all day. Karpenter provisions the cheapest instance that fits pending pods and removes nodes when they drain.
- Bin-pack workloads onto fewer, larger nodes
- Let Karpenter pick instance types and zones
- Set a sensible
consolidationpolicy
3. Use Spot for stateless workloads
Stateless services, CI runners and batch jobs are perfect for Spot instances
at 60–90% off. Keep a small On-Demand base for critical pods and let the rest
ride Spot with PodDisruptionBudgets for safety.
4. Delete the zombies
Every cluster has them: orphaned LoadBalancer services (each one an ELB you
pay for), unattached EBS volumes, and old snapshots.
# Unattached EBS volumes
aws ec2 describe-volumes \
--filters Name=status,Values=available \
--query 'Volumes[].VolumeId'
5. Make cost visible
You can't fix what nobody sees. Tag everything, then surface per-namespace cost with Kubecost or OpenCost on a Grafana dashboard the whole team looks at.
Done together, these typically take 30–40% off the bill in the first month — with no impact on reliability. The hard part isn't the tools, it's the discipline to keep doing it.
Want a free cost review of your cluster? Get in touch.