Skip to main content
RunBook Academy

KubernetesXIX · Jobs and CronJobsJobs and CronJobs

Restart policy, backoffLimit, and podFailurePolicy — Job resilience

Advanced⏱ ~17 minkubectlkubeadm

What you'll learn

  • Configure restartPolicy for the Job's workload class
  • Set backoffLimit and activeDeadlineSeconds to bound retry and duration
  • Use exponential backoff between Pod retries
  • Apply podFailurePolicy for selective Pod failure handling

Prerequisites

Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16

Not yet marked complete on this device.

A Job’s resilience is determined by five fields: the restart policy (the container-level contract), the backoffLimit (the retry budget), the activeDeadlineSeconds (wall-clock budget), the exponential backoff (time between retries), and the podFailurePolicy (selective Pod failure handling). Production Jobs configure all five; a Job without them can run forever or fail unpredictably.

restartPolicy

The container restart policy is set on the Pod template:

spec:
  template:
    spec:
      restartPolicy: OnFailure
PolicyBehaviourUse case
OnFailureContainer is restarted in the same Pod on non-zero exitMost batch workloads
NeverA failed Pod is replaced with a new PodStateful retries; container must run from clean state
AlwaysContainer is always restartedRejected by API server for Jobs

OnFailure is the default and recommended setting. The Pod’s lifecycle is owned by the controller; the container restart within the Pod is automatic.

flowchart TB
    A[Job Pod starts] --> B{Container exits?}
    B -->|0 success| C[Pod Succeeded]
    B -->|non-zero failure| D{restartPolicy}
    D -->|OnFailure| E[Restart container in same Pod]
    D -->|Never| F["Pod Failed<br/>new Pod created"]
    E --> G{retries > backoffLimit?}
    F --> G
    G -->|yes| H[Job marked Failed]
    G -->|no| I["Wait backoff<br/>retry"]

backoffLimit

spec:
  backoffLimit: 4

The number of Pod retries before the Job is marked Failed. With OnFailure, the count includes container restarts within the same Pod; with Never, each Pod is a separate retry.

ValueBehaviour
0No retries; first failure = Job Failed
44 retries before Job Failed
default6

The retry count is the total across all Pods (or restart cycles for OnFailure). A Job that has failed 4 times is Failed; the controller does not create more Pods.

activeDeadlineSeconds

spec:
  activeDeadlineSeconds: 3600

A wall-clock budget. After 3600 seconds from Job creation, the Job is marked Failed and all Pods are terminated. Unlike backoffLimit, this is independent of retry count.

The use case: a long-running batch that should not exceed an hour. After an hour, the Job is Failed regardless of how many retries have happened.

flowchart TB
    A[Job created] --> B{Elapsed > activeDeadlineSeconds?}
    B -->|yes| F1["Job Failed<br/>terminate Pods"]
    B -->|no| C[Continue]
    C --> D{Failed Pods > backoffLimit?}
    D -->|yes| F1
    D -->|no| A

Exponential backoff between retries

Since Kubernetes 1.21, Jobs support exponential backoff between Pod retries:

spec:
  backoffLimit: 10
  podRetryPolicy: Exponential    # or Never

With podRetryPolicy: Exponential, the time between Pod retries doubles on each failure. The base delay is 10 seconds; the cap is 6 minutes. A Job that fails 10 times waits approximately:

10s + 20s + 40s + 80s + 160s + 320s + ... ≈ up to 6 minutes per backoff

This is critical for batch workloads that depend on external systems (a database, a third-party API) that may be temporarily unavailable. The exponential backoff avoids hammering the dependency while still retrying.

podFailurePolicy

spec:
  backoffLimit: 4
  podFailurePolicy:
    rules:
    - action: Ignore
      onExitCodes:
        container: 42
    - action: FailJob
      onExitCodes:
        container: 137

podFailurePolicy (alpha in 1.25, beta in 1.28) lets the operator decide what to do with specific failure modes:

  • Ignore: the failure does not count against backoffLimit. Useful for “expected” failures (e.g., exit code 42 means “no work to do”).
  • FailJob: the failure marks the Job Failed immediately, regardless of retry count.
  • Count: the default — the failure counts toward backoffLimit.

The use case: a batch where some Pods return exit code 42 (“no work”) and others return 1 (“genuine error”). With podFailurePolicy, the no-work Pods do not count against the retry budget.

flowchart TB
    A[Pod fails] --> B{Exit code}
    B -->|0| C[Succeeded]
    B -->|42| D["podFailurePolicy<br/>Ignore"]
    B -->|137| E["podFailurePolicy<br/>FailJob"]
    B -->|other| F[backoffLimit counts]
    D --> G[Job continues]
    E --> H[Job Failed]
    F --> I{retries > backoffLimit?}
    I -->|yes| H
    I -->|no| J[Retry with backoff]

TTL after finished

spec:
  ttlSecondsAfterFinished: 3600

The Job (and its Pods) is deleted 3600 seconds after completion. The TTL controller sweeps completed Jobs automatically. Without TTL, completed Jobs accumulate forever.

Putting it together

A production Job manifest:

apiVersion: batch/v1
kind: Job
metadata:
  name: process-batch-20260816
spec:
  completions: 8
  parallelism: 4
  completionMode: Indexed
  backoffLimit: 5
  activeDeadlineSeconds: 7200
  ttlSecondsAfterFinished: 86400
  podRetryPolicy: Exponential
  template:
    spec:
      restartPolicy: OnFailure
      serviceAccountName: batch-runner
      containers:
      - name: worker
        image: worker:v1.2.3
        resources:
          requests:
            cpu: 500m
            memory: 1Gi
          limits:
            cpu: 1
            memory: 2Gi

Every field is set deliberately:

  • completions and parallelism define the work shape.
  • completionMode: Indexed partitions the work.
  • backoffLimit: 5 bounds retries.
  • activeDeadlineSeconds: 7200 bounds total duration.
  • ttlSecondsAfterFinished: 86400 cleans up after 24 hours.
  • podRetryPolicy: Exponential spaces retries apart.
  • restartPolicy: OnFailure is the container’s contract.

Quiz

Knowledge check · 4 questions

  1. Q1. What does backoffLimit 4 mean in a Job spec?

  2. Q2. Setting restartPolicy OnFailure with a flaky container can hit backoffLimit in seconds because container restarts count toward the retry budget.

  3. Q3. Your Job has backoffLimit 4 and activeDeadlineSeconds 3600. The Pod's container exits 1 (failure) every 10 seconds, restarting 4 times in 40 seconds. The Job is Failed. Diagnose.

    Job process-batch with backoffLimit 4, activeDeadlineSeconds 3600, restartPolicy OnFailure. The container crashes every 10 seconds due to a misconfiguration.

  4. Q4. What is podFailurePolicy and when is it useful?

Passing score: 75%. Answers are checked in this browser.

Production discipline

  • Always set backoffLimit. A Job without it retries 6 times by default — too few for transient failures, but a Job without retry is dangerous.
  • Always set activeDeadlineSeconds. A hanging Pod without a deadline runs until the cluster is exhausted.
  • Use podRetryPolicy: Exponential for external dependencies. Network or third-party API calls are flaky; hammering them on a tight retry is rude.
  • Use podFailurePolicy for expected failures. A Pod that exits with “no work” code should not count against the retry budget.
  • Set ttlSecondsAfterFinished. Completed Jobs accumulate; the TTL controller sweeps them. Without TTL, manual cleanup is the only way.

Job resilience is operator discipline. The controller provides the tools; the operator chooses the values. The Job that runs every Monday at 02:00 to process the weekend’s batch should not run forever or fill etcd with completed Job objects.