Requests are what the scheduler believes, limits are what the kernel enforces
Two numbers that look like a pair are read by two different systems for two different purposes. Requests decide placement and are a claim about the future. Limits decide runtime enforcement and are a cgroup setting. Nearly every strange capacity problem in Kubernetes comes from conflating them.
TL;DR: Resource-fit scheduling uses requests. On Linux, CPU requests also influence relative CPU weights under contention; limits bound CPU time and memory usage through cgroups. Requests too high strand capacity. Requests too low overload nodes the scheduler thinks are fine. The declarations affect QoS class, while node-pressure eviction also depends on actual usage and priority.
Requests: a claim, not a measurement
When a pod is created, the scheduler filters nodes with enough unreserved capacity and scores what remains. It reads requests. The default resource-fit calculation uses declared requests rather than live utilization; placement also considers other constraints and scheduling plugins.
That single fact explains a great deal. A fleet where every pod requests four cores and uses one will look full at 25 percent real utilisation, until you right-size requests or add capacity. Invert it and the scheduler happily packs a node whose pods collectively want far more than exists, then the kernel sorts it out, badly.
So requests are the most consequential number in a manifest, and they are usually copied from another manifest.
Limits: enforcement, and the two resources behave differently
CPU is compressible. When the cgroup exhausts its CPU quota, runnable work is throttled until quota is available again. The period is commonly 100ms but is configurable. CPU quota enforcement itself does not kill a process. Latency does, in a way that hides from average CPU graphs because most periods end under quota.
At a memory limit, the kernel first attempts reclaim. If it cannot satisfy an allocation within the cgroup boundary, a cgroup OOM can kill processes; it is not an immediate kill for every transient approach to the limit. Cgroup v2 also supports memory.high reclaim pressure and throttling, whose use depends on runtime and cluster configuration.
For a predictable service working set, equal memory requests and limits can reserve capacity conservatively. Bursty workloads may justify a lower request with a measured limit; budget aggregate node demand and the resulting eviction risk. CPU limits are contested, and the honest position is that they cost latency to buy predictability, so they belong where you have multi-tenant or cost-attribution reasons and not by reflex.
The gap between them is a policy
With container-level CPU and memory declarations, QoS classes are:
- Guaranteed requires a nonzero CPU request/limit pair and memory request/limit pair, equal within each pair, for every container.
- BestEffort has no CPU or memory requests or limits.
- Burstable covers the remaining configurations. A missing request can also default from a declared limit, so inspect the admitted pod.
QoS is not a strict eviction queue. For resource pressure, kubelet considers whether usage exceeds requests, pod priority, then usage relative to requests. Inode and PID pressure have no corresponding requests, so priority matters directly. Guaranteed pods can still be evicted when the node cannot recover. Check version-specific rules when using pod-level resource declarations.
A pod with no resource fields is not neutral, it is volunteering. That is worth saying to anyone who thinks omitting the section is the safe default.
Where the numbers should come from
Observed usage, not intuition. Request near the sustained working set with headroom for real variance, using a percentile from actual data rather than the peak, since a request sized for the once-a-quarter peak strands capacity for the other three months.
The mechanism for getting that data without letting anything act is a vertical autoscaler in recommendation mode. It watches, it suggests, it changes nothing. Comparing its recommendations against what teams actually requested is usually the fastest way to find out whether your cluster is oversubscribed or half empty, and the answer is rarely what people expect.
The failure this explains
Nodes at 90 percent while others sit idle. Pods evicted on a node with free memory. A cluster that cannot schedule anything while its real utilisation is 30 percent. p99 latency spikes on a service whose CPU graph is flat.
Compare requests, limits and observed demand when investigating these symptoms, alongside placement constraints and node-pressure signals. Similar symptoms can have different causes.
Self-check
A Guaranteed pod is running during node pressure. Is eviction impossible? No. Guaranteed is not immunity: inspect the pressure signal, usage relative to requests and priority. Resource requests also influence scheduling and CPU weights, not merely a QoS label.