Kubernetes CronJobs

The time-based scheduler for Kubernetes batch workloads. A CronJob wraps a Job with a cron expression, creating a new Job (and therefore new Pods) on every schedule tick. Synthesized from CKA Day 12 — DaemonSet, Job & CronJob Explained.

What is a CronJob?

A CronJob is a Kubernetes controller that creates Jobs on a recurring schedule. It is the cluster-native equivalent of crontab — but with the full power of Kubernetes resource management, fault tolerance, and observability.

Every time the schedule triggers, the CronJob controller spawns a new Job object. That Job creates Pods, waits for them to complete, and then reports status. Because each scheduled run is an independent Job, a failed run does not block the next scheduled execution.

Key Insight: CronJob → Job → Pod. The CronJob never creates Pods directly. It is a meta-controller that manages Job lifecycles over time.

Why Use a CronJob?

RequirementLinux crontabKubernetes CronJob
Run on a schedule
Cluster-aware resource limits
Automatic retry on failure✅ (via Job backoffLimit)
Run history and logs❌ (manual)✅ (kubectl get jobs)
Concurrency control✅ (concurrencyPolicy)
Suspend without deleting✅ (suspend: true)
History cleanup✅ (successfulJobHistoryLimit)

Canonical Use Cases

  • Scheduled backups: Database dumps, etcd snapshots, volume snapshots
  • Periodic cleanup: Prune old logs, delete temporary files, expire cache entries
  • Recurring reports: Daily sales reports, weekly analytics exports, monthly audits
  • Health checks: Periodic liveness tests, certificate expiration checks, compliance scans
  • Data synchronization: Nightly ETL syncs, batch imports from external APIs

YAML Structure

apiVersion: batch/v1
kind: CronJob
metadata:
  name: db-backup
spec:
  schedule: "0 2 * * *"  # Daily at 2:00 AM
  startingDeadlineSeconds: 3600
  concurrencyPolicy: Forbid
  successfulJobHistoryLimit: 3
  failedJobHistoryLimit: 1
  suspend: false
  jobTemplate:
    spec:
      backoffLimit: 3
      activeDeadlineSeconds: 1800
      template:
        spec:
          restartPolicy: OnFailure
          containers:
          - name: backup
            image: postgres:15
            command:
            - pg_dump
            - -h
            - db-service
            - -U
            - admin
            - -f
            - /backups/daily.sql
            volumeMounts:
            - name: backup-vol
              mountPath: /backups
          volumes:
          - name: backup-vol
            emptyDir: {}

Key fields:

FieldDefaultDescription
scheduleRequiredCron expression (standard 5-field format)
startingDeadlineSecondsunsetMax seconds after scheduled time to still start the Job
concurrencyPolicyAllowAllow (overlap), Forbid (skip if running), Replace (kill old, start new)
successfulJobHistoryLimit3Number of completed Job objects to retain
failedJobHistoryLimit1Number of failed Job objects to retain
suspendfalsePause the schedule without deleting the CronJob
jobTemplateRequiredThe Job spec template for each scheduled run

Cron Expression Format

Kubernetes uses the standard 5-field cron format:

┌───────────── minute (0 - 59)
│ ┌───────────── hour (0 - 23)
│ │ ┌───────────── day of month (1 - 31)
│ │ │ ┌───────────── month (1 - 12)
│ │ │ │ ┌───────────── day of week (0 - 6, Sunday = 0)
│ │ │ │ │
* * * * *

Common patterns:

ExpressionMeaning
*/5 * * * *Every 5 minutes
0 * * * *Every hour at minute 0
0 2 * * *Daily at 2:00 AM
0 0 * * 0Every Sunday at midnight
0 0 1 * *First day of every month at midnight

Concurrency Policies

PolicyBehaviorWhen to Use
AllowMultiple Jobs from the same CronJob can run simultaneouslyIndependent, idempotent tasks
ForbidIf a Job is still running, the next scheduled start is skippedTasks that must not overlap
ReplaceIf a Job is still running, it is terminated and a new one startsAlways need the latest data

Managing History and Cleanup

By default, Kubernetes keeps 3 successful Jobs and 1 failed Job. Older Jobs are automatically deleted. You can adjust these limits or use a ttlSecondsAfterFinished field inside the jobTemplate to delete individual Jobs (and their Pods) after a set time.

Suspending a CronJob

kubectl patch cronjob db-backup -p '{"spec":{"suspend":true}}'

This stops the schedule without deleting the CronJob definition — useful for maintenance windows or incident response.

Essential Commands

CommandPurpose
kubectl get cronjobsList CronJobs
kubectl get jobsSee Jobs created by the CronJob
kubectl describe cronjob <name>Schedule, history, and last run status
kubectl logs job/<job-name>Read logs from a specific run
kubectl delete cronjob <name>Delete CronJob and stop scheduling
kubectl create cronjob ... --dry-run=client -o yamlGenerate a manifest quickly

CKA Exam Relevance

  • Workloads & Scheduling (~15%): Create a CronJob with a given schedule. Be precise with the cron expression.
  • Troubleshooting (~30%): If a CronJob missed a run, check startingDeadlineSeconds (was the controller down too long?), concurrencyPolicy (was it Forbid and a previous Job still running?), and suspend status.
  • Speed pattern:
    kubectl create cronjob backup --image=busybox --schedule="0 2 * * *" \
      --dry-run=client -o yaml > cronjob.yaml
    # Edit jobTemplate to add volumes, env vars, and restartPolicy: OnFailure

Sources

Practical Practice

Exam-style hands-on tasks for this topic. Complete each task before reviewing the solution. Time yourself — CKA tasks average 5–7 minutes.

Task 1: Create a Daily CronJob You are asked to create a CronJob named db-backup that runs echo "backup done" every day at 2:30 AM. Requirements: Use --dry-run=client -o yaml, save to a file, and apply. Use the correct 5-field cron expression. Verification: kubectl get cronjob db-backup shows the schedule 30 2 * * *. Solution:

kubectl create cronjob db-backup --image=busybox --schedule="30 2 * * *" --dry-run=client -o yaml > db-backup.yaml
# Edit the command to: ['sh', '-c', 'echo "backup done"']
# Ensure restartPolicy: OnFailure inside the jobTemplate
kubectl apply -f db-backup.yaml

Task 2: Set Job History Limits You are asked to set successfulJobsHistoryLimit: 3 and failedJobsHistoryLimit: 2 on an existing CronJob named db-backup. Requirements: Use a single patch command; verify the limits are updated. Verification: kubectl get cronjob db-backup -o jsonpath='{.spec.successfulJobsHistoryLimit}' returns 3. Solution:

kubectl patch cronjob db-backup -p '{"spec":{"successfulJobsHistoryLimit":3,"failedJobsHistoryLimit":2}}'
kubectl describe cronjob db-backup | grep -i history

Task 3: Troubleshoot a Non-Running CronJob A CronJob named report-gen is not creating Jobs. Verify the schedule expression and startingDeadlineSeconds, then fix any issue. Requirements: Check kubectl describe cronjob report-gen for last schedule time and any warnings. Fix the schedule or deadlines. Verification: kubectl get jobs shows a new Job created by the CronJob. Solution:

kubectl describe cronjob report-gen
# Check that the schedule expression is valid and startingDeadlineSeconds is not too short
kubectl patch cronjob report-gen -p '{"spec":{"startingDeadlineSeconds":300}}'
# Or fix the schedule expression if it is malformed

Tags: kubernetes cronjob batch scheduling cka devops backup cleanup cron