Requests, not usage, drive the bill
The scheduler packs nodes based on what Pods request, not what they actually use. A Pod that requests 2 CPU but uses 200m still reserves 2 CPU worth of node capacity that nothing else can use — it's the single most common source of a cluster that "should" be over-provisioned but is somehow still full.
kubectl top pods --containers # actual usage right now
kubectl describe node <node> | grep -A5 "Allocated resources" # requested vs. capacitySet requests close to real steady-state usage (measured, not guessed) and let limits — not requests — provide headroom for spikes.
Bin packing and pool sizing
Fewer, larger nodes generally pack better than many small ones, since every node reserves some capacity for the kubelet and system daemons regardless of size — that overhead is paid once per node, not once per Pod.
Spot / preemptible nodes
Spot instances (AWS), preemptible/Spot VMs (GCP), and Spot VMs (Azure) run at a steep discount — commonly 60–90% off on-demand — in exchange for the provider being able to reclaim them with short notice. A good fit for a node pool running stateless, horizontally scaled, interruption-tolerant workloads; a poor fit for a database's StatefulSet.
# Keep latency-sensitive or stateful workloads off the spot pool entirely
spec:
template:
spec:
nodeSelector:
cloud.google.com/gke-spot: "true"
tolerations:
- key: cloud.google.com/gke-spot
operator: Equal
value: "true"
effect: NoScheduleAutoscale down, aggressively but safely
A cluster autoscaler that never scales down is just a fixed-size cluster with extra steps. Pair it with a PodDisruptionBudget on anything that shouldn't lose too many replicas at once, so scale-down evictions stay safe, and check that nothing is pinning nodes open unnecessarily (a DaemonSet-only node, or Pods without a PodDisruptionBudget that the autoscaler is being conservative around).
Reserved / committed-use discounts
For the baseline load that's never going away — the minimum node count a cluster never drops below — a 1- or 3-year commitment (Reserved Instances, Committed Use Discounts, Azure Reservations) on top of autoscaling for the variable part is usually the biggest single line-item saving available, with zero application changes required.
Where to actually look first
- Namespaces or Deployments with requests far above real usage.
- Idle non-production clusters left running outside working hours.
- Oversized persistent volumes provisioned once and never resized down.
- Cross-zone or cross-region traffic between chatty services, which cloud providers meter and charge for separately from compute.