Requests vs. Limits: Two Different Jobs
A resource request is what the scheduler uses to decide which node a Pod can fit on: kube-scheduler only places a Pod on a node that has at least that much unallocated CPU and memory, making requests fundamentally a scheduling and capacity-planning input. A resource limit is what the kubelet and container runtime enforce once the container is running: it is a hard ceiling on how much CPU or memory the container may actually consume, enforced via Linux cgroups. These are independent knobs — a Pod can request 100m CPU but be limited to 500m, meaning it is scheduled as if it needs a small amount but is allowed to burst up to five times that if the node has spare capacity.
Cricket analogy: It is like a franchise's salary-cap allocation for a player (the request, used to decide if the squad fits under the cap) being separate from the actual overs that player is allowed to bowl in a single match (the limit, enforced live by the umpire regardless of what they were budgeted for).
What Happens When You Exceed a Limit
CPU is a compressible resource: exceeding a CPU limit results in the container being throttled by the CFS (Completely Fair Scheduler) quota mechanism, meaning it simply runs slower, its process is not killed. Memory is incompressible: a container that exceeds its memory limit is immediately OOMKilled by the kernel, terminating the process (visible as OOMKilled in kubectl describe pod), and if it is part of a Deployment/StatefulSet it gets restarted, potentially entering a CrashLoopBackOff if the limit is simply too low for the workload's actual needs. This asymmetry is why memory limits deserve more careful tuning than CPU limits: an undersized CPU limit costs latency, but an undersized memory limit costs availability.
Cricket analogy: It is like a bowler being told to bowl at reduced pace once they exceed a workload cap, which slows them down but does not remove them from the attack (the CPU throttling case), versus a batter who exceeds a strict shot-selection rule and is simply given out on the spot (the memory OOMKill case), a much harsher and less recoverable outcome.
apiVersion: v1
kind: Pod
metadata:
name: api-server
spec:
containers:
- name: api
image: my-registry/api:latest
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1"
memory: "256Mi" # requests == limits for memory: Guaranteed-style safety
---
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: team-a
spec:
limits:
- default:
cpu: "500m"
memory: "512Mi"
defaultRequest:
cpu: "100m"
memory: "128Mi"
type: ContainerQoS Classes and LimitRanges
Kubernetes derives a Quality of Service class for every Pod purely from its requests and limits, with no separate field to set: Guaranteed requires every container to have requests equal to limits for both CPU and memory; Burstable applies when at least one container has a request set but it does not qualify as Guaranteed; and BestEffort applies when no container specifies any requests or limits at all. This class directly determines eviction order under node memory pressure — the kubelet evicts BestEffort Pods first, then Burstable Pods (ranked by how far usage exceeds requests), and only evicts Guaranteed Pods as an absolute last resort, which is why production-critical workloads are usually configured as Guaranteed. Because forgetting to set requests/limits is easy and defaults to BestEffort (the worst QoS class), namespace administrators commonly define a LimitRange to inject sane default requests and limits onto any container that omits them.
Cricket analogy: It is like ranking which players are the last to be dropped from a squad during a tough tour, where every all-rounder pulling equal weight in both batting and bowling budgets (Guaranteed) is protected far longer than a specialist with only a rough estimate on one discipline (Burstable), while anyone with no defined role at all (BestEffort) is dropped first.
The eviction order under node memory pressure is BestEffort, then Burstable (worst overage first), then Guaranteed only as a last resort. This is one of the strongest arguments for always setting at least a memory request, even if you never set a limit: it moves a workload out of BestEffort into Burstable and meaningfully improves its survival odds during a real incident.
Setting a memory limit lower than a container's actual steady-state usage does not throttle it the way a CPU limit would — it gets OOMKilled and restarted, potentially in a loop. Always base memory limits on observed usage (ideally via VPA recommendations or profiling) rather than guessing, since memory has no safe 'slow down' behavior the way CPU does.
- Requests drive scheduling (which node a Pod fits on); limits are runtime enforcement ceilings via cgroups.
- CPU is compressible: exceeding a limit throttles the process via CFS quota, it is not killed.
- Memory is incompressible: exceeding a limit gets the container OOMKilled by the kernel, immediately.
- QoS class (Guaranteed, Burstable, BestEffort) is derived automatically from requests/limits, not set directly.
- Node eviction order under memory pressure is BestEffort first, then worst-offending Burstable, then Guaranteed last.
- A LimitRange in a namespace can inject default requests/limits so forgetting them doesn't silently default to BestEffort.
- Guaranteed QoS requires requests == limits for both CPU and memory on every container in the Pod.
Practice what you learned
1. What is the primary purpose of a resource request in Kubernetes?
2. What happens when a container exceeds its CPU limit?
3. What happens when a container exceeds its memory limit?
4. What are the requirements for a Pod to receive the Guaranteed QoS class?
5. In what order does the kubelet evict Pods under node memory pressure?
Was this page helpful?
You May Also Like
Horizontal Pod Autoscaling
Learn how Kubernetes automatically scales the number of Pod replicas in and out based on CPU, memory, and custom metrics.
Vertical Pod Autoscaling
Understand how VPA right-sizes container CPU and memory requests automatically based on observed usage history.
Node Affinity and Taints
Learn how node affinity pulls Pods toward specific nodes while taints and tolerations repel or dedicate nodes for specific workloads.