Full lesson
Explore the full explanation, examples, and visuals at your own pace.
kubectl get pods
Three Pods keep restarting, and the RESTARTS column keeps climbing. That number only tells you something died, not why. The exit code is the first clue. Each of these containers left one behind, and decoding it tells you where to look next.
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
api-7d9f 0/1 CrashLoopBackOff 6 (40s ago) 12m
worker-5c2b 1/1 Running 4 (2m ago) 30m
web-8f4a 1/1 Running 3 (5m ago) 1hWhere the exit code lives
In kubectl describe, State is the container running right now. Last State is the one that died. Read Reason and Exit Code together: here, OOMKilled and 137. The same fields live in the Pod status, under containerStatuses, lastState, terminated, if you'd rather script it.
$ kubectl describe pod worker-5c2b
worker:
State: Running
Last State: Terminated
Reason: OOMKilled
Exit Code: 137
Restart Count: 4
# same data in the Pod status
$ kubectl get pod worker-5c2b -o jsonpath='{.status.containerStatuses[0].lastState.terminated}'Decode rule
An exit code splits into two ranges. From 1 to 127, the app picked the number itself when it quit. Above 128, a signal ended the process, so subtract 128 to get the signal number. That one subtraction tells you who or what stopped the container.
api-7d9f: exit code 1
Take api-7d9f. It starts, tries to load its config, and that step fails. The app throws an error and exits with code 1 on its own. No signal killed it. The app made that choice, so the explanation is in its own output.
Read the dead container's logs
Exit code 1 means the app chose to fail, so ask it why. The --previous flag matters, because it shows logs from the crashed run, not the fresh restart. Here, the output says DATABASE_URL is missing, and that's the reason it exited with 1. This output is illustrative.
$ kubectl logs api-7d9f --previous
Error: missing env var DATABASE_URL
at loadConfig (config.js:12)worker-5c2b: 137 = 128 + 9
Now the worker. Its memory climbs past the limit, and the kernel steps in with SIGKILL, signal nine. The process gets no chance to clean up. Kubelet records the death as 137, because 128 plus 9 is 137, and the reason says OOMKilled. That confirms memory. Then kubelet restarts the container.
Same 137, different cause
Exit code 137 only tells you SIGKILL was sent. It doesn't say why. On the left, memory crosses the limit and the container is killed instantly. On the right, it ignored a stop request, the grace period ran out, and it was force-killed. Always read Reason beside the code.
A container shows exit code 137, but Reason says Error instead of OOMKilled. What is the most likely cause?
A container shows exit code 137, but Reason says Error instead of OOMKilled. What is the most likely cause?
- It ran out of memory
- It was force-killed after the grace period
- The app returned a generic error itself
A container shows exit code 137, but Reason says Error instead of OOMKilled. What is the most likely cause?
It was force-killed after the grace period. 137 means SIGKILL, signal nine, so something outside the app killed it. Without OOMKilled in the Reason field, memory is not the confirmed cause; a common alternative is a forced kill after the termination grace period ran out.
- It ran out of memory
- It was force-killed after the grace period
- The app returned a generic error itself
web-8f4a: 143 = 128 + 15
Now web-8f4a. Exit code 143 is 128 plus 15, and 15 is SIGTERM, a polite request to stop. In the sequence, the liveness probe fails, the Kubelet sends SIGTERM, web shuts down with 143, and then it restarts. So when you see 143 with restarts, check Events in describe for probe failures.
A clue, not a diagnosis
An exit code narrows the search, but it never proves the root cause. Apps can return any code they like. So confirm it with Reason and Events, then read the logs with kubectl logs --previous.
- Apps can return any exit code
- Pair code with Reason and Events
- Then read logs with --previous
1 = app error, 137 = SIGKILL, 143 = SIGTERM
Above 128, a signal killed it, so subtract 128 to name the signal. Api's 1 was the app's own error. Worker's 137 is signal 9, and web's 143 is signal 15. Then check Reason to confirm.




