Kubernetes Requests and Limits: How to Right-Size Pods Without Breaking Reliability Kubernetes requests and limits determine pod scheduling and runtime resource enforcement, but the Cast AI 2026 State of Kubernetes Optimization Report finds average CPU utilization across production clusters is just 8%, measured directly from tens of thousands of clusters. The gap between requested and actual usage drives both cost and stability risk, and the guide explains how to derive correct values from real data and enforce them at scale. Kubernetes requests and limits are the two resource controls that determine where pods get scheduled, how much CPU and memory they can consume, and what happens when they exceed those boundaries. According to the Cast AI 2026 State of Kubernetes Optimization Report https://cast.ai/reports/state-of-kubernetes-optimization/ , average CPU utilization across production clusters sits at just 8%. That 8% figure comes from direct measurement across tens of thousands of production clusters; not survey estimates. That gap between what teams request and what workloads actually use costs real money and creates real stability risk. This guide walks through the mechanics, the failure modes, how to derive correct values from real data, and how to enforce them at scale. Requests vs. Limits: How Kubernetes Schedules Pods What Each Setting Actually Does A request is the amount of CPU or memory Kubernetes guarantees to a container. The kube-scheduler uses requests, and only requests, to decide which node can host a pod. If a node’s allocatable CPU minus its total scheduled requests is less than the incoming pod’s CPU request, the scheduler skips that node entirely. A limit is the maximum a container can consume. Limits are invisible to the scheduler. They only matter after the pod is running. Once a container hits its CPU limit, the kernel throttles it. Once it hits its memory limit, the kernel kills it. The table below captures the functional difference between the two settings. | Property | Requests | Limits | |---|---|---| | What it does | Guaranteed minimum for scheduling and resource reservation | Maximum the container can consume at runtime | | Used for scheduling | Yes — scheduler places pods based on requests | No — invisible to kube-scheduler | | CPU enforcement mechanism | cpu.shares cgroup v1 / cpu.weight cgroup v2 : proportional share under contention | cpu.cfs quota us / cpu.cfs period us cgroup v1 / cpu.max cgroup v2 : hard cap per CFS period | | Memory enforcement mechanism | Soft guarantee via QoS eviction priority | Hard cap: container is OOM killed on breach | | Failure mode if set too high | Pods stuck in Pending; wasted capacity; unnecessary node scale-out | N/A high limits are permissive, not dangerous | | Failure mode if set too low | Pods scheduled incorrectly; resource starvation under contention | CPU throttling P99 latency spikes or OOMKilled exit code 137 | QoS Classes: Eviction Priority Under Pressure Kubernetes assigns every pod a Quality of Service QoS class based on how requests and limits are configured. This class determines which pods the kubelet evicts first when a node runs low on memory. | QoS Class | Condition | Eviction Priority | |---|---|---| | Guaranteed | Every container in the pod has memory limit = memory request AND cpu limit = cpu request | Last — evicted only under critical node pressure | | Burstable | At least one container has requests or limits set, but the pod does not meet Guaranteed criteria | Middle — evicted after BestEffort pods are gone | | BestEffort | No container in the pod has any requests or limits set | First — evicted immediately under any memory pressure | Guaranteed pods have an additional benefit: they qualify for exclusive CPU allocation via the static CPU management policy. For latency-sensitive workloads, this eliminates the CPU sharing overhead that causes jitter under load. A note on eviction precision: the kubelet’s QoS-based eviction ordering applies when it detects memory pressure and proactively evicts pods. However, the Linux OOM killer which acts when the kernel itself runs out of memory uses oomScoreAdj scores that only partially align with QoS class. Under extreme kernel-level memory pressure, Kubernetes can kill a Guaranteed pod before a Burstable pod that uses less memory. The OOM killer targets the process with the highest combined score, which accounts for both the adjustment and actual RAM consumption. BestEffort pods are always killed first. How Over-Setting Requests Wastes Resources and Money The 2026 Cast AI State of Kubernetes Optimization Report measured 69% of requested CPU going unused across production clusters. That’s up from 40% the prior year the waste is getting worse, not better . Memory overprovisioning sits at 79%. These are not outliers from small teams. These are fleet-wide averages across organizations running Kubernetes at scale. Why Teams Overprovision The root cause is straightforward: overprovisioning feels safe. Requesting more CPU prevents throttling. Requesting more memory prevents OOM kills. However, the cost compounds silently across every pod, every namespace, and every cluster. Over-setting requests hurts in two concrete ways. First, the cluster autoscaler sees nodes as full before they actually are, so new nodes spin up based on scheduled requests, not actual usage. Second, the scheduler can starve other pods when inflated requests make nodes appear full on paper. The Scale Problem Consider a workload that uses 500m CPU at p95 but has a request of 2 cores. That pod runs 4x overprovisioned. At 500 replicas across a cluster, that is 750 wasted cores. Those cloud costs add a meaningful line item to the monthly bill before a single engineer does any optimization work. At 500 nodes, the compounding is more severe. Overprovisioned requests cause the cluster autoscaler to hold 20 to 30% more nodes than the workload needs. Because autoscaler decisions are request-driven, not usage-driven, accurate requests are the prerequisite for efficient autoscaling. The autoscaling guides for HPA https://cast.ai/blog/what-is-kubernetes-hpa-and-how-can-it-help-you-save-on-the-cloud/ and cluster-level autoscaling https://cast.ai/blog/guide-to-kubernetes-autoscaling-for-cloud-cost-optimization/ both assume requests that reflect real usage. Failure Modes: OOMKilled Exit Code 137 and CPU Throttling Under-setting requests and limits produces two distinct failure modes. Both are preventable with the right values. Both are diagnosable with the right tooling. OOMKilled: Memory Limit Breached When a container exceeds its memory limit, the Linux OOM killer sends SIGKILL signal 9 to the process. The exit code is 128 + 9 = 137. Kubernetes reports the container status as OOMKilled . Confirm it with: kubectl describe pod