The Restart Loop Problem
Deployments, StatefulSets, and DaemonSets share a fundamental assumption: the workload should run forever. If a Pod exits — for any reason — Kubernetes restarts it. This is exactly what you want for a web server. It's catastrophic for a database migration script.
Consider what happens if you run a migration script in a Deployment:
- The script runs and completes successfully (exit code 0).
- Kubernetes sees the container exited and thinks it crashed.
- Kubernetes restarts the Pod.
- The migration runs again. Depending on how it's written, this might be harmless (idempotent) or it might corrupt your database (non-idempotent).
You need a workload type that understands the concept of "done."
How Jobs Work
A Job creates one or more Pods and tracks their completion. It interprets exit codes:
- Exit code 0: The task succeeded. The Job marks the Pod as
Completedand does not restart it. - Non-zero exit code: The task failed. The Job recreates the Pod to retry, up to the
backoffLimit(default: 6). The retry delay doubles each time (exponential backoff): 10s, 20s, 40s, 80s...
A Job manifest sets a retry limit (backoffLimit) and an overall time limit (activeDeadlineSeconds) on top of an otherwise ordinary Pod template — the one required difference being restartPolicy: Never (or OnFailure), which is required for Jobs. It tells the kubelet not to restart the container when it exits — the Job controller handles retry logic at the Pod level instead.
Parallel Jobs
Jobs support parallelism for batch workloads. Two key fields control this:
completions: Total number of successful Pod completions required before the Job is considered done.parallelism: Maximum number of Pods running simultaneously.
This is useful for processing a queue of work items — for example, resizing 1,000 images by running 10 worker Pods in parallel, each processing 100 images.