Kubernetes requests are a purchase order
The scheduler places pods by what they request, not by what they use. A pod that requests 2 CPUs and uses 0.2 still takes 2 CPUs of space on a node. The autoscaler adds nodes to make room for requests. The bill follows the nodes.
So the most important cost setting in Kubernetes isn’t a pricing plan. It’s the requests in each deployment, usually copied from a template years ago.
Measure the gap
Compare requested resources with actual use for each workload over at least a week, using a high percentile rather than the average:
# CPU actually used (p95 over a week), per container
quantile_over_time(0.95,
rate(container_cpu_usage_seconds_total{container!=""}[5m])[7d:5m])
# CPU requested, per container
kube_pod_container_resource_requests{resource="cpu"}
Workloads using under a third of their request are your easy wins. Across a cluster, total requests at two or three times total usage is common. That’s two or three times the nodes you need.
CPU and memory are different problems
- CPU can be shared. A container that goes over its CPU request can borrow idle CPU. If it hits a CPU limit, it gets throttled: it slows down rather than crashing, which shows up as unexplained latency. Many teams set CPU requests and skip CPU limits for latency-sensitive services, so pods can burst into spare capacity.
- Memory can’t be shared. A container that goes over its memory limit is killed. For memory, set the request to what the service really needs at peak, and set the limit equal to the request. Predictable beats clever.
Let the cluster pack tightly
Right-sized requests only save money if nodes are removed afterwards. Node autoscalers with consolidation, such as Karpenter on EKS, move pods off underused nodes and remove them. That only works if pods can move: set PodDisruptionBudgets, avoid local storage where you can, and make shutdown graceful.
Automate the recommendations
The Vertical Pod Autoscaler in recommendation mode watches real usage and suggests requests without changing anything. Review its output weekly, or feed it into your manifests through pull requests. Kubernetes 1.33 also turned on in-place pod resize as a beta feature by default, so changing a pod’s resources doesn’t always mean restarting it.
Make the cost visible per team
Requests are set by application teams. Nodes are paid for by the platform team. That split is why waste survives. Show each namespace its requested CPU and memory, its actual use, and the gap, in money. Tools like OpenCost can allocate cluster cost by namespace and label.
When a team can see that their service reserves eight nodes and uses two, the next pull request tends to fix it.