⚡ ~/naveed Interview Prep
⚡ Portfolio Home ✍️ Engineering Blog Deep Dives 🎯 Interview Hub 1,000+ Scenarios ☸️ Kubernetes Mastery Hub 24 Modules 🎮 DevOps Arcade & Quizzes Subnet Blitz ⚡ 🗺️ DevOps Roadmaps PDFs & Guides 🤖 Morpheus Analysis AI Quant ↗ 🛠️ Developer Tools Utilities 🧪 Labs & Experiments 📄 Interactive CV & Certs 🔗 All Links & Socials ⚡ Join The Dispatch (Weekly SRE Newsletter) →
← Back to All Kubernetes Interview Questions Scenario 203 of 203 in Kubernetes
Senior DevOps / SRE Kubernetes Cluster Troubleshooting & SRE Scenarios Production Scenario
🎯 Target Role / Context: Senior SRE / Performance Architect Interview Question

Q: A high-volume microservice repeatedly crashes with 'OOMKilled' and Exit Code 137, but the underlying worker node has 48GB of unallocated physical RAM. Explain why this happens, how Linux cgroup memory accounting works in Kubernetes, how Kubelet assigns oom_score_adj based on QoS classes, and how you resolve OOMKilled in JVM and Node.js applications.

Authoritative senior SRE guide to demystifying OOMKilled (Exit Code 137), dissecting Linux cgroups v1 vs v2 memory accounting, container-level limits vs node-level OS memory pressure, JVM container awareness, and memory leak triage.

#Kubernetes #OOMKilled #Exit Code 137 #Cgroups v2 #Memory Limits #JVM Tuning #SRE Observability #Prometheus
🎙️ Candidate Opening & Architectural Context
"In Linux and Kubernetes, Exit Code 137 is calculated as 128 + 9 (SIGKILL). It indicates that the process was unconditionally terminated by the Linux kernel OOM killer. Crucially, a container can be OOMKilled even when the host node has gigabytes of free physical RAM. This happens because Kubernetes enforces resource boundaries through Linux Control Groups (cgroups). When the container's memory working set breaches its cgroup 'memory.max' (cgroups v2) or 'memory.limit_in_bytes' (cgroups v1), the kernel fires SIGKILL specifically at that container's processes."
Advertisement
⚡ Recommended Practice Lab

Want to master this scenario in a live sandbox? KodeKloud's CKA & CKAD Hands-On Certification Track covers this exact problem with hands-on terminal drills.

🛠️ Production Runbook & Step-by-Step Resolution

1️⃣

Status Verification & Exit Code 137 Classification

Confirm that the termination was indeed an Out-Of-Memory kill and extract the historical termination state:

  • Describe the Pod's Last State: Run kubectl describe pod <pod-name> and check Last State: Terminated.
    Look for: Reason: OOMKilled and Exit Code: 137.
  • Container OOM vs Node OOM:
    • Container OOM: Node has free RAM. Kubelet condition MemoryPressure is False. The container breached its specific pod memory limit.
    • Node OOM: The worker node ran completely out of physical RAM and swap. Kubelet condition MemoryPressure is True. The host kernel OOM killer sacrificed pods based on oom_score_adj.
Pro Tip: Always check both the container's Last State and the node's MemoryPressure condition to distinguish between container-limit OOM and node-exhaustion OOM.
2️⃣

Linux Cgroup Memory Accounting: Working Set vs RSS

Understand how Kubernetes measures memory consumption to prevent false diagnoses:

  • The Metric That Triggers OOM: Kubernetes does NOT base OOM kills on Resident Set Size (RSS) alone! It evaluates container_memory_working_set_bytes.
    working_set = memory.current - inactive_file.
  • The Page Cache Trap: Linux caches file reads in the page cache (active_file + inactive_file). While inactive page cache can be reclaimed under memory pressure, dirty and active file pages cannot be reclaimed instantly.
  • Prometheus PromQL Query:
    container_memory_working_set_bytes{pod="<pod-name>"} / 
    kube_pod_container_resource_limits{resource="memory", pod="<pod-name>"} * 100
Pro Tip: Monitoring tools tracking only process RSS will show 60% memory usage while the pod gets OOMKilled at 100% due to active file page cache consumption.
Advertisement
3️⃣

Kubelet Quality of Service (QoS) & oom_score_adj

How the Linux kernel decides which process to terminate first during node-level memory exhaustion:

  • Guaranteed (requests == limits for CPU & Memory): Kubelet assigns oom_score_adj = -997. The kernel will almost never terminate this process unless absolute panic occurs.
  • Burstable (requests < limits): Kubelet calculates: oom_score_adj = min(max(2, 1000 - (1000 * memoryRequest) / totalNodeMemory), 999). Evicted before Guaranteed.
  • BestEffort (no requests or limits): Kubelet assigns oom_score_adj = 1000. These are the absolute first processes killed by the kernel during host memory pressure.
Pro Tip: Production databases and critical microservices should always be assigned the Guaranteed QoS class (requests == limits).
4️⃣

The Java / JVM Container Memory Blindness Trap

Why Java applications are the #1 source of OOMKilled incidents in enterprise Kubernetes:

  • The Host Core & RAM Blindness: Older JVMs (prior to Java 8u191 / Java 11) query /proc/meminfo instead of cgroup limits. On a 64GB node with a 2GB container limit, the JVM calculates default heap as 25% of 64GB = 16GB, instantly triggering OOMKilled at startup!
  • Non-Heap Memory Consumption: Total JVM Memory = Heap + Metaspace + Thread Stacks + Direct Memory + Native Memory. Sizing heap to 1.8GB inside a 2.0GB container will always cause OOMKilled.
  • JVM Flag Solution:
    JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -XX:InitialRAMPercentage=50.0 -XX:+ExitOnOutOfMemoryError"
Pro Tip: Never set -Xmx equal to container memory limit. Reserve at least 25-30% of the container memory budget for JVM non-heap, metaspace, and thread stacks.
5️⃣

Node.js, Go & Python Memory Limit Tuning

Tuning other modern language runtimes for container cgroup boundaries:

  • Node.js V8 Heap: Node.js defaults to ~1.4GB heap. In smaller containers (e.g. 1GB limit), configure: NODE_OPTIONS="--max-old-space-size=768".
  • Golang GOMEMLIMIT (Go 1.19+): Set GOMEMLIMIT=900MiB for a 1GiB container. The Go GC runs aggressively as memory approaches this ceiling, preventing OOMKilled.
  • Python Memory Leaks: Profile memory leaks using tracemalloc and verify that data processing pipelines stream files rather than reading entire multi-gigabyte payloads into memory.
Pro Tip: Go's GOMEMLIMIT soft memory limit drastically reduces OOMKilled events by triggering garbage collection before hitting cgroup thresholds.
6️⃣

Vertical Pod Autoscaler (VPA) & Hands-On Lab

Automate memory sizing and practice real OOMKilled incident triage:

  • Deploy Vertical Pod Autoscaler (VPA): Run VPA in updateMode: "Off" (recommendation mode) to analyze real-world peak memory usage over 14 days before setting production limits.
  • Set Meaningful Memory Requests: Set memory request equal to 95th percentile working set, and memory limit to 125-150% of request to accommodate traffic spikes.
  • Lab Runbook: Practice profiling memory leaks, JVM container tuning, and cgroups v2 debugging in DevOps Lab: OOMKilled Hands-On Runbook ↗.
Pro Tip: Never set memory limits without memory requests; doing so automatically downgrades the pod to the Burstable QoS tier.
💡 The Senior SRE Gold Nugget (Key Architectural Takeaway)
"Exit Code 137 (OOMKilled) is triggered when container_memory_working_set_bytes exceeds the cgroup limit, regardless of host RAM. Differentiate container limit OOM from node OS memory pressure via node conditions. Size JVM heap to no more than 75% of container limit (-XX:MaxRAMPercentage=75.0), leverage GOMEMLIMIT in Go, and assign Guaranteed QoS to critical stateful services."
⚡ 60-Second Elevator Pitch Talking Points
  • Verified Exit Code 137 (128 + 9 SIGKILL) in 'kubectl describe pod' and checked node conditions to confirm container-level cgroup limit breach rather than host RAM exhaustion.
  • Analyzed 'container_memory_working_set_bytes' in Prometheus, recognizing that active page cache is included in the kernel's cgroup kill threshold.
  • Evaluated Kubelet QoS tiers and oom_score_adj assignments (Guaranteed vs Burstable vs BestEffort) to protect critical workloads during node pressure.
  • Tuned runtime container memory awareness: configured '-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0' for JVM applications and 'GOMEMLIMIT' for Go.
  • Deployed Vertical Pod Autoscaler (VPA) in recommendation mode to establish data-driven memory requests and limits based on p99 traffic peaks.
  • Eliminated in-memory streaming bottlenecks in application code to permanently prevent memory leak regressions.
Advertisement
📥 FREE DOWNLOAD · 101-PAGE COMPANION HANDBOOK
Studying for Kubernetes & SRE Technical Rounds?
Download the complete 100-question PDF field guide covering all 11 core modules with offline diagnostic runbooks.
📥 Download PDF (Free) Read Online Guide →
Want more Kubernetes scenarios?
Explore our complete collection of scenario-based Kubernetes interview runbooks.
Browse All Kubernetes Questions →