Kubernetes — Job fails with BackoffLimitExceeded
Diagnose BackoffLimitExceeded by reading pod failure reasons and deciding between fixing the job and adjusting retries.
Symptoms
- Job has reached the specified backoff limit
- Pods in the job restart and finally fail
- The job succeeds locally but fails in the cluster
- Only some pods fail while others succeed
Possible causes
- The container command fails deterministically
- Resource limits cause OOMKilled or eviction
- A dependency is temporarily unavailable so every retry fails
- The job uses a wrong image or missing configuration
Diagnosis steps
Describe the job and its pods
kubectl describe job <name> ; kubectl get pods -l job-name=<name>Interpretation
The reason field on failed pods distinguishes a container error from OOMKilled or eviction, pointing at code versus resources.
Read the failed container logs
kubectl logs <pod> --previousInterpretation
Logs from the previous attempt show the application error that caused each retry to fail.
Check exit codes and limits
kubectl get pod <pod> -o jsonpath="{.status.containerStatuses[*].lastState}"Interpretation
An OOMKilled last state indicates the memory limit is too low rather than a code bug.
Root cause
The job exhausted its retry budget because the container failed repeatedly, either from a deterministic application error or from resource limits that killed it, so the controller stopped retrying and marked the job failed.
Fix
Fix the underlying failure: correct the command, image or configuration, or raise memory limits where pods are killed. Adjust backoffLimit and restartPolicy only when failures are genuinely transient.
Verify the fix
Rerun the job and confirm it completes with the expected number of successful pods, and confirm no pods are OOMKilled.
Symptom walkthrough
A Job ends as failed with the reason BackoffLimitExceeded, meaning its pods failed more times than the retry budget allows. The key question is why they fail: a deterministic application error never recovers, while an OOMKilled pod points at memory limits and an unavailable dependency may be transient. Which one applies changes the fix from code to resources to retry policy.
Fix in practice
Read the last state of failed pods and their previous logs to classify the failure, then act accordingly: fix the command, image or configuration, raise memory limits when pods are killed, or extend retries only for genuinely transient failures. Increasing backoffLimit without a fix just wastes time.
Verification
Rerun the job and confirm it completes with the expected successful pod count, then confirm none of the pods were OOMKilled. A successful job with healthy pods confirms the real cause is fixed.
Diagnostic commands
Check the behavior with commands like these:
kubectl describe job <name> ; kubectl get pods -l job-name=<name>
kubectl logs <pod> --previous
kubectl get pod <pod> -o jsonpath="{.status.containerStatuses[*].lastState}"