Guaranteed QoS — The Highest Protection Level for Critical Workloads
What is Guaranteed QoS in Simple Terms?
Guaranteed is the highest QoS class Kubernetes assigns. A pod with Guaranteed QoS gets a reserved seat — the node commits that this pod will always have its requested CPU and memory, regardless of what other pods are doing. When the node runs low on memory and starts evicting pods, Guaranteed pods are evicted last.
Use Guaranteed for your most critical workloads — databases, payment processors, trading engines — where eviction means a direct user impact that you cannot accept.
The Exact Rule for Guaranteed Classification
For a pod to get Guaranteed QoS, EVERY container (including init containers and sidecar containers) must meet ALL of these conditions:
+------------------------------------------+| Container must have: || || requests.cpu SET and EQUAL to || limits.cpu || || requests.memory SET and EQUAL to || limits.memory || || If ANY container is missing ANY of these || the ENTIRE pod is classified Burstable || not Guaranteed |+------------------------------------------+A Correctly Configured Guaranteed Pod
apiVersion: apps/v1kind: Deploymentmetadata: name: trading-engine namespace: productionspec: replicas: 3 template: spec: containers: # Main container — must have requests == limits * name: trading-engine image: registry.zerodha.in/trading-engine:v4.2.1 resources: requests: cpu: "2" # 2 full CPU cores requested memory: "4Gi" # 4GB RAM requested limits: cpu: "2" # EXACTLY equal to request — Guaranteed memory: "4Gi" # EXACTLY equal to request — Guaranteed # Sidecar container — ALSO must have requests == limits # If this sidecar has no resources, the whole pod becomes Burstable * name: metrics-exporter image: prom/statsd-exporter:v0.24.0 resources: requests: cpu: "100m" memory: "64Mi" limits: cpu: "100m" # Equal to request memory: "64Mi" # Equal to request# Verify the pod got Guaranteed classificationkubectl get pod trading-engine-7d9f8c-xkp2q -n production \ -o jsonpath='{.status.qosClass}'# Output: Guaranteed # If it shows Burstable instead, one container has mismatched values# Find which container is breaking the rule:kubectl get pod trading-engine-7d9f8c-xkp2q -n production -o json | \ jq '.spec.containers[] | {name: .name, resources: .resources}'When to Use Guaranteed vs Burstable
+------------------------------------------+| Use Guaranteed QoS for: || || * Databases (PostgreSQL, MySQL) || Cannot afford eviction — data loss || || * Payment processors || Eviction = failed transactions || || * Trading engines (Zerodha) || Eviction during market hours = crisis || || * Prometheus / AlertManager || Eviction = monitoring blackout |+------------------------------------------+ +------------------------------------------+| Use Burstable QoS for: || || * Web API servers || Can restart quickly, stateless || || * Background workers || Can retry failed jobs || || * Most microservices || Short restart is acceptable |+------------------------------------------+The Cost of Guaranteed — Node Packing Efficiency
Guaranteed QoS has a cost. Because requests equal limits, the scheduler treats the full limit as reserved capacity even if the pod is only using 20% of it. This reduces how many pods fit on a node.
| Node: 16 CPU available|| Scenario A — All Burstable:| * 8 pods with requests=1 CPU, limits=4 CPU| * Scheduler sees 8 CPU reserved (8 x 1 request)| * 8 pods fit on the node| * Pods can burst to 4 CPU each when node is idle|| Scenario B — All Guaranteed:| * 4 pods with requests=4 CPU, limits=4 CPU| * Scheduler sees 16 CPU reserved (4 x 4 CPU)| * Only 4 pods fit on the node| * Node appears full even if pods use only 2 CPU eachThis is why Guaranteed QoS should be selective — only for truly critical workloads that cannot tolerate eviction. Using Guaranteed everywhere makes your cluster expensive and difficult to pack efficiently.
Checking All Guaranteed Pods in the Cluster
# List all Guaranteed pods across all namespaceskubectl get pods -A -o json | \ jq -r '.items[] | select(.status.qosClass=="Guaranteed") | [.metadata.namespace, .metadata.name] | @tsv' | \ column -t # Check QoS class for all pods in a namespace at oncekubectl get pods -n production \ -o custom-columns=NAME:.metadata.name,QOS:.status.qosClass # Describe a pod to confirm Guaranteed classificationkubectl describe pod trading-engine-7d9f8c-xkp2q -n production | grep QoS# QoS Class: GuaranteedWhat Happens During Node Eviction With Guaranteed Pods
+------------------------------------------+| Node memory pressure event || Available memory drops to 180Mi || (below eviction threshold of 200Mi) |+------------------------------------------+ | v+------------------------------------------+| kubelet scans all pods by QoS class: || || BestEffort pods -> killed immediately || (analytics-worker, cache-warmer) || || If still under pressure: || Burstable pods -> killed by usage % || (pods using most above their request) || || Last resort only: || Guaranteed pods -> killed by usage % || (trading-engine, payment-processor) |+------------------------------------------+In practice, on a well-configured cluster with proper ResourceQuotas and LimitRanges, Guaranteed pods should almost never be evicted — the BestEffort and Burstable evictions bring memory below pressure first.
REMEMBER THIS**Remember:** If even ONE container in a pod has mismatched requests and limits, the entire pod is classified as Burstable — not Guaranteed. Check every sidecar container, every init container, and every injected sidecar (like Istio's Envoy proxy) — these are often injected automatically and may not have matching requests and limits, silently downgrading your pod from Guaranteed to Burstable.
COMMON MISTAKE / WARNING**Common Mistake:** Setting Guaranteed QoS on every pod in the cluster because it sounds safer. This destroys bin packing efficiency. A node with 32 CPU running 32 Guaranteed pods each requesting 1 CPU appears fully packed to the scheduler — even if every pod is only using 100m. You end up needing 10x more nodes than necessary. Reserve Guaranteed for genuinely critical stateful workloads only.
PLACEMENT PRO TIP**Tip:** For Istio-enabled clusters at Hotstar or Swiggy scale, the Envoy sidecar is automatically injected into every pod. To maintain Guaranteed QoS on pods with Istio injection, you must also set resources.requests == resources.limits on the injected `istio-proxy` container. Do this by setting global Istio proxy resource values in the Istio operator configuration — otherwise every pod in the mesh is silently downgraded to Burstable.