Fix Kubernetes OOMKilled: Pod Keeps Getting Killed for Memory
The Error: OOMKilled (Exit Code 137)
You run kubectl get pods and see:
NAME READY STATUS RESTARTS AGE
myapp-6b8f9d4c7-x2k9j 0/1 OOMKilled 5 12m
Or in kubectl describe pod:
State: Terminated
Reason: OOMKilled
Exit Code: 137
This means the Linux kernel's Out-Of-Memory (OOM) killer terminated your container because it exceeded its memory limit.
Root Cause
Kubernetes enforces memory limits set in your pod spec. When a container tries to allocate memory beyond its resources.limits.memory value, the kernel kills the process with signal 9 (SIGKILL). Common causes:
- Memory limit set too low for the workload
- Application memory leak (especially in long-running services)
- JVM heap not aligned with container memory limit
- Sidecar containers consuming shared memory budget
Step-by-Step Fix
1. Check current memory usage and limits
# See the pod's resource configuration
kubectl describe pod <pod-name> -n <namespace> | grep -A 5 "Limits\|Requests"
# Check actual memory usage across pods
kubectl top pods -n <namespace> --sort-by=memory
2. Review recent memory consumption
# Get previous container logs (before it was killed)
kubectl logs <pod-name> -n <namespace> --previous
# Check events for OOM history
kubectl get events -n <namespace> --sort-by='.lastTimestamp' | grep OOM
3. Increase the memory limit
Edit your deployment manifest:
resources:
requests:
memory: "256Mi" # Scheduler guarantee
cpu: "100m"
limits:
memory: "512Mi" # Hard ceiling — OOMKilled if exceeded
cpu: "500m"
Apply the change:
kubectl apply -f deployment.yaml
4. For JVM applications, align heap with container limits
The JVM does not automatically respect container memory limits in older versions. Set heap explicitly:
env:
- name: JAVA_OPTS
value: "-XX:MaxRAMPercentage=75.0 -XX:+UseContainerSupport"
This tells the JVM to use at most 75% of the container's memory limit, leaving room for non-heap memory (metaspace, thread stacks, NIO buffers).
5. Profile memory to find leaks
If the limit seems adequate, the application may be leaking:
# Port-forward and attach a profiler
kubectl port-forward <pod-name> 8080:8080
# For Node.js, enable heap snapshots
node --inspect=0.0.0.0:9229 app.js
# For Go, expose pprof
# Import _ "net/http/pprof" and hit /debug/pprof/heap
6. Use a VPA to right-size automatically
# Install Vertical Pod Autoscaler, then create a recommendation
cat <<EOF | kubectl apply -f -
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: myapp-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
updatePolicy:
updateMode: "Off" # "Off" = recommend only, "Auto" = apply changes
EOF
# Check recommendations
kubectl describe vpa myapp-vpa
Prevention Tips
- Set requests close to actual usage and limits 20-50% above requests to absorb spikes.
- Never omit memory limits in production — unbounded pods can destabilize the entire node.
- Monitor with Prometheus: alert on
container_memory_working_set_bytesapproaching limits. - Use
--previouslogs immediately after an OOMKill to capture the state before restart. - For JVMs, always set
-XX:+UseContainerSupport(default in JDK 11+) and capMaxRAMPercentage.
Was this article helpful?
Senior Kubernetes Architect
10+ years orchestrating containers in production. Battle-tested opinions on everything from pod scheduling to service mesh. I've seen clusters burn and helped rebuild them better.
Related Articles
Systematic Debugging of CrashLoopBackOff: A Field Guide From Someone Who's Been Paged Too Many Times
A systematic approach to debugging CrashLoopBackOff in Kubernetes, covering the most common causes and the exact commands to diagnose each one.
Fix Kubernetes 'Evicted' Pods Filling Up the Node
Clean up Kubernetes evicted pods and fix the underlying disk pressure or resource exhaustion that causes pod evictions.
Fix Kubernetes ImagePullBackOff: Container Image Won't Pull
Resolve ImagePullBackOff and ErrImagePull errors in Kubernetes — fix registry credentials, image tags, and network access issues.
Istio Observability and Authorization: Distributed Tracing, Metrics, and Access Policies
How to use Istio's built-in observability — distributed tracing with Jaeger, Prometheus metrics, Kiali service graph — and enforce zero-trust access control with AuthorizationPolicies.
Istio Service Mesh: Installation, Traffic Management, and mTLS
A practical guide to getting started with Istio — installing on Kubernetes, enabling automatic mTLS, configuring VirtualServices for traffic management, and understanding the sidecar injection model.
The Complete Guide to Kubernetes Deployment Strategies: Rolling, Blue-Green, Canary, and Progressive Delivery
A comprehensive guide to every Kubernetes deployment strategy — rolling updates, blue-green, canary, and progressive delivery with Argo Rollouts and Flagger.
More in Kubernetes
View all →Kubernetes Vertical Pod Autoscaler: Automating Resource Request Tuning In Production
Let me be direct with you: most Kubernetes clusters I audit are hemorrhaging money because of poorly configured resource requests. I've seen teams running...
Fix Helm 'UPGRADE FAILED: has no deployed releases'
Fix the Helm 'UPGRADE FAILED: has no deployed releases' error when a previous install failed and left the release in a broken state.
Running Kafka on Kubernetes with Strimzi Operator
A hands-on guide to deploying and operating Kafka on Kubernetes using the Strimzi operator — covering cluster config, topic management, and TLS security.
Kubernetes Network Policies: Implementing Zero-Trust Pod Communication
How to implement zero-trust pod-to-pod communication in Kubernetes using NetworkPolicies — deny by default, allow explicitly, and validate it works.