19.4 QoS Classes: Guaranteed, Burstable, BestEffort
Alright, let’s talk about Quality of Service (QoS) classes. This is where Kubernetes stops being a polite container orchestrator and starts acting like a brutally honest bouncer at an overbooked nightclub. It has to decide which of your pods get the VIP treatment, which get to wait in the general admission line, and which might get unceremoniously kicked to the curb to make room for a bigger spender when things get hectic.
The class assigned to your pod isn’t something you set directly with a qosClass: field. Oh no, that would be too straightforward. Instead, Kubernetes assigns it automatically based on the resource requests and limits you deign to provide in your Pod spec. It’s a judgment, and you’re being judged based on how well you fill out your paperwork.
How QoS Classes Are Determined
Think of it like a tax bracket. The system looks at your CPU and memory requests and limits and slots you into one of three categories. Let’s break down the rules.
Guaranteed: This is the platinum-plated, first-class ticket. To qualify here, your pod must be a bit obsessive-compulsive:
- Every container in the pod must have both a CPU and memory request and a limit.
- For each container, the request must equal the limit for both CPU and memory.
Why the strictness? Because you’re telling the scheduler, “I need exactly this much, and I will never, ever need more. Pinky swear.” Kubernetes can then allocate that exact amount of resources and know, with certainty, that this pod will never be a noisy neighbor. These pods are the last to be evicted under memory pressure.
apiVersion: v1
kind: Pod
metadata:
name: guaranteed-pod
spec:
containers:
- name: main-app
image: nginx
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "64Mi" # Must equal the request
cpu: "250m" # Must equal the request
Burstable: This is the most common class and, frankly, where most of us live. You get some guarantees, but you’re also allowed to, well, burst. You qualify if:
- The pod does not meet the criteria for
Guaranteed. - At least one container in the pod has a CPU or memory request set.
This is the “I mostly need this much, but please, for the love of all that is holy, let me use more if it’s just sitting there idle” option. Your pod’s minimum needs (requests) are guaranteed, but it can consume resources up to its limit if available. The catch? Under system memory pressure, these pods are higher on the eviction list than Guaranteed pods.
apiVersion: v1
kind: Pod
metadata:
name: burstable-pod
spec:
containers:
- name: web-server
image: nginx
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi" # Can be higher than the request
cpu: "500m" # Can be higher than the request
BestEffort: This is the free trial with no credit card required. It’s the wild west. You get this class if:
- No container in the pod has any CPU or memory requests or limits.
You’re telling Kubernetes, “I’ll run wherever you can squeeze me in, and I’ll take whatever scraps are left over.” These pods can consume any amount of CPU on a node (until they’re throttled) and any amount of memory (until the kubelet decides to kill them to free some up). They are, unsurprisingly, the absolute first to be terminated when the node gets resource-constrained. Do not run your database like this.
apiVersion: v1
kind: Pod
metadata:
name: best-effort-pod
spec:
containers:
- name: careless-process
image: busybox
command: ['sh', '-c', 'while true; do echo "Living on the edge!"; sleep 10; done']
# No resources section. We live and die by chaos.
Why This Matters in the Real World
It’s not just about eviction order, though that’s the big one. The QoS class also influences how the kubelet handles your pod’s Out Of Memory (OOM) killer score on the Linux node. A BestEffort pod gets a high score, making it a prime target for the OOM killer. A Guaranteed pod gets a very low score, shielding it. Burstable is somewhere in the middle, based on how much of its requested memory it’s using.
The most common pitfall I see? Forgetting that this is determined per-pod, not per-container. If you have a pod with a tightly constrained Guaranteed main app container and a sidecar with no limits, congratulations, you’ve just demoted the entire pod to Burstable. Kubernetes judges you by your worst-behaved child.
So, be intentional. Use Guaranteed for your critical, steady-state workloads that need predictability. Use Burstable for pretty much everything else—it’s the sensible default. And avoid BestEffort unless you’re running a completely ephemeral, meaningless job that you wouldn’t mind losing to a light breeze.