Jobs Basics
This section covers the Kubernetes Job object: how to run a container to completion, control retries and timeouts, and clean up after the work finishes.
Prerequisites
- A working cluster on Kubernetes 1.36.2 with
kubectlconfigured against it. - A container image built with Docker Engine 29.6.1 (BuildKit) and pushed to a registry your nodes can pull.
- containerd as the CRI runtime on nodes runs the Pods; Docker is only the build tool here.
kubectl version --client
kubectl get nodesBasic Examples
1. A minimal Job
The smallest useful Job runs one Pod to completion.
apiVersion: batch/v1
kind: Job
metadata:
name: hello
spec:
template:
spec:
restartPolicy: Never
containers:
- name: hello
image: busybox:1.36
command: ["sh", "-c", "echo done && exit 0"]apiVersion: batch/v1is the stable API for Jobs.- The Pod template must set
restartPolicytoNeverorOnFailure;Alwaysis rejected. - With no
completionsorparallelism, the Job succeeds after one Pod exits 0. - Apply with
kubectl apply -f job.yamland watch withkubectl get job hello -w.
2. Inspecting Job status
Read the Job and its Pods to see progress and results.
kubectl get job hello
kubectl describe job hello
kubectl logs job/hellokubectl get jobshowsCOMPLETIONSas succeeded over desired, like1/1.kubectl logs job/hellostreams logs from a Pod owned by the Job.describesurfaces events such asSuccessfulCreateand the finalCompletedcondition.
3. restartPolicy: Never vs OnFailure
The restart policy changes how a failed attempt is retried.
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: worker
image: myrepo/worker:1.4.0Nevercreates a fresh Pod per attempt, so each try has its own logs.OnFailurerestarts the container in the same Pod, creating fewer objects.- Use
Neverwhen you want clean per-attempt debugging and event history. - Use
OnFailurewhen Pod churn or scheduling latency is a concern.
4. Limiting retries with backoffLimit
Cap how many times a failing Job retries before it gives up.
spec:
backoffLimit: 4
template:
spec:
restartPolicy: Never
containers:
- name: worker
image: myrepo/worker:1.4.0backoffLimitdefaults to 6 if you omit it.- Each failure adds exponential backoff, capped at six minutes, before the next attempt.
- When the limit is exceeded, the Job gets a
Failedcondition and stops. - Set this deliberately so a broken image cannot retry indefinitely.
5. Bounding runtime with activeDeadlineSeconds
Put a hard wall-clock cap on the whole Job.
spec:
activeDeadlineSeconds: 600
backoffLimit: 4
template:
spec:
restartPolicy: Never
containers:
- name: worker
image: myrepo/worker:1.4.0activeDeadlineSecondscounts from when the Job starts, across all retries.- Exceeding it terminates running Pods and marks the Job
Failedwith reasonDeadlineExceeded. - This protects against a process that hangs instead of crashing.
- It takes priority over
backoffLimitonce the deadline passes.
6. Automatic cleanup with ttlSecondsAfterFinished
Delete finished Jobs and their Pods automatically.
spec:
ttlSecondsAfterFinished: 3600
template:
spec:
restartPolicy: Never
containers:
- name: worker
image: myrepo/worker:1.4.0- The TTL controller removes the Job 3600 seconds after it completes or fails.
- Deleting the Job cascades to its Pods, freeing etcd and node resources.
- Without this field, finished Jobs accumulate until you delete them by hand.
- Keep the window long enough to collect logs, short enough to avoid clutter.
7. Setting requests, limits, and a non-root user
Batch Pods still need resource requests and a hardened security context.
spec:
template:
spec:
restartPolicy: Never
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: worker
image: myrepo/worker:1.4.0
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
memory: "512Mi"requestsdrive scheduling and let the scheduler pack Pods onto nodes.- A
memorylimit prevents one batch Pod from starving co-tenants. runAsNonRootplusRuntimeDefaultseccomp aligns with the restricted Pod Security Standard.- Build the image itself to run as a non-root UID so this succeeds.
Intermediate Examples
8. Fixed completions with parallelism
Run a fixed number of successful Pods, several at a time.
apiVersion: batch/v1
kind: Job
metadata:
name: batch-import
spec:
completions: 10
parallelism: 3
backoffLimit: 6
template:
spec:
restartPolicy: Never
containers:
- name: importer
image: myrepo/importer:2.1.0completions: 10means the Job needs ten Pods to exit 0.parallelism: 3runs up to three Pods concurrently.kubectl get job batch-importshows progress like7/10.- Each Pod does an equivalent chunk of work; see Indexed and Parallel Jobs for per-Pod inputs.
9. Suspending and resuming a Job
Create a Job without starting it, then release it later.
spec:
suspend: true
template:
spec:
restartPolicy: Never
containers:
- name: worker
image: myrepo/worker:1.4.0- With
suspend: true, no Pods are created until you flip it. - Resume with
kubectl patch job worker -p '{"spec":{"suspend":false}}'. - Suspending a running Job deletes its active Pods and pauses the count.
- This is how schedulers and CronJobs hold work back until a gate opens.
10. Handling specific failures with podFailurePolicy
React differently to specific container exit codes.
spec:
backoffLimit: 6
podFailurePolicy:
rules:
- action: FailJob
onExitCodes:
containerName: worker
operator: In
values: [42]
template:
spec:
restartPolicy: Never
containers:
- name: worker
image: myrepo/worker:1.4.0podFailurePolicylets you fail the whole Job fast on an unrecoverable exit code.- Here exit code 42 triggers
FailJobinstead of consuming retry budget. - Other actions include
Ignore, which does not count a failure againstbackoffLimit. - This avoids wasting retries on errors that will never succeed.
Stack versions: This page was written for Kubernetes 1.36.2, Docker Engine 29.6.1 (BuildKit default), containerd (CRI runtime on nodes), Helm 3, Compose v2, Argo CD (latest - verify at build), and Gateway API (GA - verify controller support at build).