CKAD — Handling Job Failures

2 minute read

Handling Job Failures

Jobs can fail — containers crash, commands return non-zero exit codes, or tasks take too long. Kubernetes provides several mechanisms to control failure behavior.

backoffLimit

Controls how many times Kubernetes retries failed Pods before giving up:

apiVersion: batch/v1
kind: Job
metadata:
  name: retry-job
spec:
  backoffLimit: 3
  template:
    spec:
      containers:
      - name: flaky
        image: busybox:1.36
        command: ["/bin/sh", "-c", "exit 1"]
      restartPolicy: Never

After 3 failed attempts, Kubernetes stops retrying and marks the Job as failed.

kubectl apply -f retry-job.yaml

# Watch the retries
kubectl get pods -l job-name=retry-job -w

# After failures, check the Job status
kubectl describe job retry-job | grep -A 5 "Pods Statuses"

How backoffLimit Works

backoffLimit Total Pods Created Behavior
0 1 No retries — fail immediately
3 4 (1 original + 3 retries) Retry 3 times with exponential backoff
6 (default) 7 (1 original + 6 retries) Default retry behavior

Kubernetes uses exponential backoff for retries: 10s, 20s, 40s, up to 6 minutes max.

activeDeadlineSeconds

Sets a total time limit for the Job. If the Job does not finish in time, Kubernetes terminates all running Pods:

apiVersion: batch/v1
kind: Job
metadata:
  name: timed-job
spec:
  activeDeadlineSeconds: 30
  template:
    spec:
      containers:
      - name: worker
        image: busybox:1.36
        command: ["/bin/sh", "-c", "echo 'Working...' && sleep 60"]
      restartPolicy: Never

This Job will be killed after 30 seconds, even though the container tries to sleep for 60.

kubectl apply -f timed-job.yaml

# Wait 30 seconds, then check
kubectl describe job timed-job | tail -5
# Expected: "Job was active longer than specified deadline"

Important: activeDeadlineSeconds overrides backoffLimit. Even if retries remain, the Job is terminated when the deadline expires.

restartPolicy: Never vs OnFailure

Policy Behavior What Happens on Failure
Never Failed Pod is left as-is. New Pod created for retry. You get multiple Pods (good for debugging)
OnFailure Failed container restarts in the same Pod. Logs are lost on restart (harder to debug)

When to Use Each

# Use Never — keeps failed Pods for log inspection
spec:
  template:
    spec:
      restartPolicy: Never

# Use OnFailure — fewer Pods, but harder to debug
spec:
  template:
    spec:
      restartPolicy: OnFailure

Exam tip: Use Never when you need to inspect failed Pod logs. Use OnFailure when you want to keep Pod count low.

Auto-Cleanup with TTL

After a Job completes, the Pod sticks around (in Completed status) so you can check logs. Use ttlSecondsAfterFinished to auto-delete:

apiVersion: batch/v1
kind: Job
metadata:
  name: auto-cleanup-job
spec:
  ttlSecondsAfterFinished: 60
  template:
    spec:
      containers:
      - name: task
        image: busybox:1.36
        command: ["/bin/sh", "-c", "echo done"]
      restartPolicy: Never

60 seconds after the Job finishes, both the Job and its Pods are deleted automatically.

TTL Behavior

ttlSecondsAfterFinished Behavior
Not set (default) Job and Pods remain until manually deleted
0 Delete immediately after completion
60 Delete 60 seconds after completion
3600 Delete 1 hour after completion

Failure Handling Summary

Mechanism Controls Example
backoffLimit Number of retry attempts backoffLimit: 3 — give up after 3 retries
activeDeadlineSeconds Total time allowed activeDeadlineSeconds: 300 — kill after 5 min
restartPolicy How failed containers restart Never = new Pod, OnFailure = same Pod
ttlSecondsAfterFinished When to clean up after done 60 — delete 1 min after Job finishes

Next: Learn about scheduled tasks with CronJobs →

Updated: