RCWRCW IT TrainingFree hands-on labs & simulators← Back to home
Kubernetes · Troubleshooting guide

Kubernetes Workload Failures: CrashLoopBackOff, ImagePullBackOff, Pending and OOMKilled

Four pod states cause most of the pain in Kubernetes. This guide gives you a repeatable diagnosis workflow for each, with the commands to run and the log lines that tell you the real cause.

Published August 29, 2026 · RCW IT Training

Start with the pod, not the cluster

Every pod failure investigation starts the same way: get the pod's own account of events before touching nodes or networking.

kubectl get pod <name> -n <ns> -o wide
kubectl describe pod <name> -n <ns>      # Events section is the gold
kubectl logs <name> -n <ns> --previous    # the crashed attempt, not the waiting one

describe shows scheduling and pull events; logs --previous shows why the process died. Ninety percent of cases end here.

CrashLoopBackOff: the process starts, then dies

What it means.

The container ran and exited (or was killed) repeatedly, and kubelet is now backing off restarts. The exit code in describe tells you which family of problem you have:

Last State / exit codeUsual cause
Exit Code 1Application error: bad config, missing env var, failed migration, unhandled exception
Exit Code 137 (SIGKILL)OOMKilled by the cgroup, or a liveness probe killing the process
Exit Code 139 (SIGSEGV)Segmentation fault — often wrong binary/architecture (amd64 image on arm64 node)
Exit Code 0 in a loopThe container "succeeds" and stops — a long-running app packaged as a one-shot command
The 60-second check sequence.
kubectl logs <pod> -n <ns> --previous --tail=100
kubectl get events -n <ns> --sort-by=.lastTimestamp | tail
kubectl get pod <pod> -n <ns> -o jsonpath='{.status.containerStatuses[0].lastState}'

Typical culprits in the logs: connection refused to a database that is not ready (add an initContainer or startup probe), ConfigMap "x" not found, or a missing secret key that crashes the app at boot.

ImagePullBackOff: the image never arrives

Read the exact message in Events.

The event text distinguishes the four causes instantly:

Event textCause and fix
manifest not found / not foundWrong tag or repository name. Verify with crane manifest or the registry UI
unauthorized / 401Missing or wrong imagePullSecret; the ServiceAccount has no secret attached
x509: certificate signed by unknown authorityPrivate registry with an internal CA — add the CA to the node's trust store or mark the registry insecure at the container runtime
context deadline exceeded / timeoutsNode cannot reach the registry: egress firewall, proxy, or DNS
Verify from the node itself.
# on the node where the pod is scheduled
crictl pull <image>          # containerd
systemctl status containerd
curl -sI https://registry.example.com/v2/

If crictl pull fails with a TLS error but curl works, the runtime's trust store is the problem, not the network.

Pending pods: nothing is wrong with the pod, something is wrong with capacity

Pending means the scheduler has not placed it.

The Events section will say exactly why. The common ones:

  • 0/5 nodes are available: 3 node(s) had taint {...}, 2 node(s) didn't match Pod's node affinity — taints/tolerations or affinity rules exclude every node.
  • Insufficient cpu / Insufficient memory — requests exceed what is schedulable. Compare with kubectl describe nodeAllocated resources.
  • pod has unbound immediate PersistentVolumeClaims — the PVC is unbound; the StorageClass may not exist or the provisioner is failing. Check kubectl get pvc and the provisioner's logs.
Capacity math that surprises people.

A node with 4 vCPU does not offer 4 vCPU to pods; kubelet and system daemons reserve part of it. If requests across the namespace total near the allocatable value, the next pod goes Pending even though top shows the node idle — scheduling uses requests, not usage.

kubectl describe node <node> | sed -n '/Allocated resources/,/Events/p'

OOMKilled: the kernel or kubelet killed the container

Tell the two OOMs apart.

Exit Code 137 with Reason: OOMKilled in lastState means the cgroup limit was hit — the container exceeded its own resources.limits.memory. A node-level OOM (kernel oom-killer under system pressure) shows in dmesg on the node and may kill any process, not just the limited one.

kubectl get pod <pod> -o jsonpath='{.status.containerStatuses[*].lastState.terminated}'
# on the node:
dmesg -T | grep -i 'out of memory'
Right-size instead of blindly doubling.
  • Check real usage over a week: kubectl top pod now, but use metrics-server history or Prometheus container_memory_working_set_bytes for the p99.
  • Set requests near observed p50 and limits near p99 + headroom. Working-set spikes during GC or large batch reads are the usual killers.
  • Java workloads: the JVM heap is not the whole story — set -XX:MaxRAMPercentage and leave 25–30% for metaspace, threads and off-heap.

Probes: when Kubernetes kills a healthy app

A container restarting with Exit Code 137 but no OOMKilled reason is usually the kubelet enforcing a failing liveness probe. Check describe for Liveness probe failed: HTTP probe failed with statuscode 503. Fixes: give slow-starting apps a startupProbe with generous failureThreshold, point liveness at a cheap endpoint (not one that checks the database), and never let liveness and readiness share an expensive handler.

Prevention checklist

  • Ship logs to a collector — kubectl logs --previous is lost after the next restart.
  • Set requests and limits on every workload; unbounded pods are the root of both Pending and node OOM drama.
  • Use startupProbe for anything that takes over 10 seconds to boot.
  • Tag images immutably (SHA or semver), never latest in production — ImagePullBackOff investigations double when tags move.
  • Alert on kube_pod_status_phase{phase="Pending"} older than 5 minutes and on kube_pod_container_status_restarts_total rate.
Key takeaway: The pod's Events section and the previous container's logs resolve most Kubernetes failures in under a minute. Exit codes classify the crash family, event text classifies pull and scheduling failures, and requests-vs-allocatable math explains Pending pods that look like mysteries.