Pod QoS Classes
When a Node runs low on memory, which Pods does Kubernetes sacrifice first? At creation the API reads the resources of a Pod and assigns a QoS class: Guaranteed when every container, or the Pod-level block, sets CPU and memory with requests equal to limits, BestEffort when none is set, Burstable in between. The class is fixed for the life of the Pod and picks the cgroup tier and the oom_score_adj, but not the eviction order, which goes by usage over request.
Play the animation 5 steps, animated, best on a wide screen
Pod QoS Classes, step by step
-
The classification rule has three outcomes, read from the containers, or from the Pod-level resources block when the Pod sets one. BestEffort: no container sets a CPU or memory request or limit. Guaranteed: every container has CPU and memory requests and limits set above zero, with requests equal to limits. Burstable: anything in between (at least one CPU or memory value set, but not the Guaranteed pattern).
Play from step 1 -
The API server applies the rule and tags each Pod with its class on status.qosClass. Pod A becomes BestEffort (empty resources). Pod B becomes Burstable (requests only, no limits). Pod C becomes Guaranteed (requests equal limits everywhere). This tag is set once at creation and never changes for the rest of the Pod life.
Play from step 2 -
Each Pod is now placed on a Node. The resource fit looks only at requests, ignoring both limits and the QoS class. Pod A asks for nothing, so CPU and memory never block it. Pod B competes for 500m CPU and 256Mi memory. Pod C competes for 1 CPU and 1Gi memory. Once a Node passes the checks, the Pod is bound to it via POST .../pods/{name}/binding.
Play from step 3 -
Kubelet on the chosen Node writes the Linux cgroup config for each Pod. The container memory cap (memory.max) and CPU cap (cpu.max) come from limits. Neither Pod A nor Pod B sets any, so neither gets a cap of its own. Kubelet also sets oom_score_adj per container, an offset the OOM killer adds to the kernel score of each process. BestEffort gets 1000 and Guaranteed gets -997, with node-critical Pods on -997 whatever their class, and Burstable in between, scaled by its memory request via 1000 - 1000*(request/capacity) and clamped to 3..999.
Play from step 4 -
When the Node runs low on memory, Kubelet ranks Pods by whether each is using more than it requested, then by Pod Priority, then by how far over the request it sits. Pod A requested nothing, so it is over the moment it allocates anything. Pod B is over its request too and, at equal Priority, goes second as it sits less far over. Pod C requests exactly its limit, so it never exceeds its request and is evicted last, ranked by Priority, if system daemons overrun their reserved share. QoS class does not decide this order, it only predicts it, and it is separate from priority-based preemption.
Play from step 5