K8s by Example: Jobs & CronJobs

Jobs run a task to completion. Unlike Deployments that run indefinitely, Jobs ensure a specified number of Pods successfully complete. Use for: migrations, batch processing, backups, one-time tasks.

job.yaml

Job runs Pods until successful completion. restartPolicy must be Never or OnFailure (required). backoffLimit: 3 retries 3 times before marking failed.

apiVersion: batch/v1
kind: Job
metadata:
  name: db-migrate
spec:
  template:
    spec:
      containers:
        - name: migrate
          image: my-app:v1
          command: ["python", "migrate.py"]
      restartPolicy: Never
  backoffLimit: 3
job-parallel.yaml

Job completion modes: NonIndexed (default) runs N identical Pods. Indexed assigns each Pod a unique index (0 to completions-1) via JOB_COMPLETION_INDEX env var for work partitioning.

spec:
  completions: 10
  parallelism: 3
  completionMode: Indexed
  template:
    spec:
      containers:
        - name: worker
          image: worker:v1
          command: ["./process-partition.sh"]
      restartPolicy: Never
job-lifecycle.yaml

backoffLimit: 3 retries then Job fails. activeDeadlineSeconds: 600 = must complete in 10 min. ttlSecondsAfterFinished: 3600 = delete 1 hour after. OnFailure restarts container in same Pod; Never creates new Pod for each retry.

spec:
  backoffLimit: 3
  activeDeadlineSeconds: 600
  ttlSecondsAfterFinished: 3600
  template:
    spec:
      restartPolicy: OnFailure
job-modes.yaml

Parallel Jobs have three modes. First example: fixed completion count (run 10 total, 3 at a time). Second example: work queue (no completions = Pods coordinate, done when any Pod exits 0). Third example: single Pod (default).

spec:
  completions: 10
  parallelism: 3
---
spec:
  parallelism: 5
---
spec:
  completions: 1
  parallelism: 1
cronjob.yaml

CronJobs run Jobs on a schedule using cron syntax (minute hour day-of-month month day-of-week). Schedule examples in code. Use concurrencyPolicy to control overlapping runs.

apiVersion: batch/v1
kind: CronJob
metadata:
  name: nightly-backup
spec:
  schedule: "0 2 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: backup
              image: backup:v1
          restartPolicy: OnFailure
cronjob-policy.yaml

concurrencyPolicy: Allow (default) allows concurrent Jobs, Forbid skips if previous running, Replace kills previous. successfulJobsHistoryLimit: 3 keeps 3 completed. startingDeadlineSeconds: 60 must start within 60s of schedule.

spec:
  schedule: "*/10 * * * *"
  concurrencyPolicy: Forbid
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 1
  startingDeadlineSeconds: 60
terminal

Suspend and resume CronJobs. Trigger manual runs from existing CronJob template. View Job history and logs.

$ kubectl patch cronjob nightly-backup \
    -p '{"spec":{"suspend":true}}'
cronjob.batch/nightly-backup patched

$ kubectl patch cronjob nightly-backup \
    -p '{"spec":{"suspend":false}}'
cronjob.batch/nightly-backup patched

$ kubectl create job manual-backup --from=cronjob/nightly-backup
job.batch/manual-backup created
terminal

Debug Jobs by checking Pod status, logs, and completion status. Failed Jobs show backoff events. CronJobs show scheduled vs actual run times.

$ kubectl get jobs
NAME           COMPLETIONS   DURATION   AGE
db-migrate     1/1           45s        5m
manual-backup  0/1           2m         2m

$ kubectl describe job db-migrate

$ kubectl logs job/db-migrate
Migration completed successfully

$ kubectl get cronjobs
NAME             SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE
nightly-backup   0 2 * * *     False     0        12h

$ kubectl get jobs -l app=nightly-backup

Index | GitHub | Use arrow keys to navigate |