Skip to main content

Guaranteed

A Kubernetes QoS Class assigned to pods where every container has CPU and memory requests set exactly equal to their limits. Guaranteed pods receive the highest resource protection and are the last to be evicted when a node runs out of memory.

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:

◈ DIAGRAM
+------------------------------------------+
| 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

YAML
apiVersion: apps/v1
kind: Deployment
metadata:
name: trading-engine
namespace: production
spec:
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
Bash
# Verify the pod got Guaranteed classification
kubectl 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

◈ DIAGRAM
+------------------------------------------+
| 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.

TEXT
| 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 each

This 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

Bash
# List all Guaranteed pods across all namespaces
kubectl 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 once
kubectl get pods -n production \
-o custom-columns=NAME:.metadata.name,QOS:.status.qosClass
# Describe a pod to confirm Guaranteed classification
kubectl describe pod trading-engine-7d9f8c-xkp2q -n production | grep QoS
# QoS Class: Guaranteed

What Happens During Node Eviction With Guaranteed Pods

◈ DIAGRAM
+------------------------------------------+
| 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

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

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.

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.

Frequently Asked Questions

Why does Kubernetes treat Guaranteed pods as the last to be evicted?

Because requests equal limits, the scheduler knows exactly how much CPU and memory the pod will ever consume, with no burst headroom. That predictability is what earns eviction priority: under node memory pressure, kubelet evicts BestEffort pods first, then Burstable pods exceeding their requests, and only reaches Guaranteed pods if the node is critically starved. It's a reliability guarantee purchased with zero flexibility.

What's the practical downside of making every pod Guaranteed QoS?

You lose bin-packing efficiency. Since requests equal limits, the scheduler reserves full peak capacity for every pod even if actual usage is usually much lower, so nodes fill up on paper long before they're busy in practice. Reserve Guaranteed QoS for genuinely critical, steady-state workloads (databases, control-plane components) and use Burstable for everything with variable load, or you'll pay for capacity you rarely use.