Skip to content

CronJobs in Kubernetes — Complete Guide

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about CronJobs in Kubernetes. We cover key concepts, practical examples, and best practices to help you master this topic.

Use Kubernetes CronJobs for cluster-level scheduling: define CronJob resources, configure job templates, handle concurrency, set timezones, manage history limits, and monitor job execution.

What You Learn

You will learn how to create and manage Kubernetes CronJob resources, configure job templates with resource limits, handle concurrency policies, manage job history, set timezone-aware schedules, and monitor CronJob execution.

Why It Matters

Kubernetes CronJobs are the standard way to run scheduled tasks in container Orchestration. Unlike traditional cron, they provide declarative configuration, automatic retry, concurrency control, and integration with Kubernetes monitoring.

Real-World Use

DodaTech uses Kubernetes CronJobs for database backups (daily), cache warming (every 30 minutes), certificate renewal (monthly), and data cleanups (hourly). CronJobs run on the same cluster as applications, sharing secrets, configmaps, and network policies.

Basic CronJob Definition

apiVersion: batch/v1
kind: CronJob
metadata:
  name: daily-backup
  namespace: production
spec:
  schedule: "0 3 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: dodatech/backup-tool:1.0
            command:
            - /usr/local/bin/backup.sh
            env:
            - name: DB_HOST
              value: postgres-service
            - name: BACKUP_BUCKET
              value: s3://dodatech-backups
            resources:
              requests:
                memory: "256Mi"
                cpu: "250m"
              limits:
                memory: "512Mi"
                cpu: "500m"
          restartPolicy: OnFailure
kubectl apply -f cronjob-basic.yaml
kubectl get cronjobs
kubectl get jobs
kubectl describe cronjob daily-backup
kubectl logs job.batch/daily-backup-123456
kubectl delete cronjob daily-backup

CronJob with Concurrency Policy

apiVersion: batch/v1
kind: CronJob
metadata:
  name: cache-warming
spec:
  schedule: "*/30 * * * *"
  concurrencyPolicy: Forbid
  startingDeadlineSeconds: 120
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 1
  jobTemplate:
    spec:
      backoffLimit: 2
      activeDeadlineSeconds: 300
      template:
        spec:
          containers:
          - name: warmer
            image: dodatech/cache-warmer:1.0
            env:
            - name: REDIS_HOST
              value: redis-service
          restartPolicy: Never

ConcurrencyPolicy options: Allow (default) multiple simultaneous jobs, Forbid skip if previous still running, Replace cancel running and start new.

Timezone-Aware CronJobs

apiVersion: batch/v1
kind: CronJob
metadata:
  name: business-hours-report
spec:
  schedule: "0 9 * * 1-5"
  timeZone: "America/New_York"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: reporter
            image: dodatech/reporter:1.0
            command: ["/usr/local/bin/generate-report.sh"]
            env:
            - name: TZ
              value: "America/New_York"
          restartPolicy: Never

For clusters without timeZone support, set TZ in containers or use init containers to configure timezone.

Suspending and Starting CronJobs

# Suspend a CronJob (stop future executions)
kubectl patch cronjob daily-backup -p '{"spec":{"suspend":true}}'

# Resume a CronJob
kubectl patch cronjob daily-backup -p '{"spec":{"suspend":false}}'

# Manually create a Job from a CronJob
kubectl create job --from=cronjob/daily-backup manual-backup-001

# Check missed schedules
kubectl get events --field-selector involvedObject.kind=CronJob

Monitoring CronJobs

# Watch CronJob status
kubectl get cronjob --watch

# View job history
kubectl get jobs --selector=job-name=daily-backup

# Check pod status for recent job
kubectl get pods --selector=job-name=daily-backup-28374651

# View logs from last job run
kubectl logs job.batch/daily-backup-28374651

# Set up alerts with Prometheus
# Use kube-state-metrics for CronJob metrics:
# kube_cronjob_status_active
# kube_cronjob_status_last_schedule_time
# kube_job_status_failed
# PrometheusRule alert for failed CronJobs
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: cronjob-alerts
spec:
  groups:
  - name: cronjob
    rules:
    - alert: CronJobFailed
      expr: time() - kube_cronjob_status_last_schedule_time > 120
      for: 5m
      labels:
        severity: critical
      annotations:
        summary: "CronJob {{ $labels.cronjob }} failed"

Common Mistakes

1. No Resource Limits

Without resource limits, a CronJob pod can consume all node resources. Always set CPU and memory requests and limits.

2. Ignoring History Limits

Successful/failed jobs accumulate and fill etcd. Set successfulJobsHistoryLimit and failedJobsHistoryLimit to 3 or fewer.

3. Wrong restartPolicy

CronJob pods must use restartPolicy: OnFailure or Never. restartPolicy: Always is invalid for Jobs.

4. Not Handling ImagePullBackoff

If the image is not found, the job never runs. Ensure images are available in the cluster's container registry.

5. Missing BackoffLimit

Without backoffLimit, a failing job retries forever. Set a reasonable limit (2-3 retries).

Practice Questions

1. What is the difference between a Job and a CronJob?

A Job runs a task once. A CronJob creates Jobs on a schedule. CronJob adds the schedule, concurrency, and history management on top of Job.

2. How do you prevent overlapping CronJob executions?

Set concurrencyPolicy: Forbid. This skips a new execution if the previous one is still running.

3. What does successfulJobsHistoryLimit control?

The number of completed Job resources to retain. Old Jobs are garbage collected. Default is 3.

4. How do you manually trigger a CronJob?

Use kubectl create job --from=cronjob/name manual-job-name to create an ad-hoc Job from the CronJob template.

Challenge

Deploy a Kubernetes CronJob that: runs every 15 minutes, has a 10-minute timeout, retries up to 3 times on failure, forbids concurrency, keeps last 2 successful and 1 failed job, sets resource limits of 128Mi memory and 100m CPU, and writes logs to stdout for kubectl logs.

FAQ

Can I update a CronJob while it is running?

Yes. Changes take effect for the next scheduled run. Currently running Jobs are not affected by CronJob updates.

What happens if a CronJob misses its schedule?

If the controller is down, missed schedules are caught up only within startingDeadlineSeconds (default 10s). Jobs missed beyond that are skipped.

How do I pass secrets to a CronJob?

Use the same Secret and ConfigMap references as regular pods. Mount them as volumes or environment variables in the job template.

Can a CronJob run on a specific node?

Yes. Use nodeSelector, affinity, or tolerations in the pod template spec, just like any other pod.

How many CronJobs can a Kubernetes cluster handle?

There is no hard limit, but each CronJob creates Job resources that consume etcd storage. For 1000+ CronJobs, monitor etcd performance.

Mini Project: CronJob Management

apiVersion: batch/v1
kind: CronJob
metadata:
  name: db-maintenance
  namespace: production
spec:
  schedule: "0 4 * * 0"
  concurrencyPolicy: Forbid
  startingDeadlineSeconds: 300
  successfulJobsHistoryLimit: 2
  failedJobsHistoryLimit: 1
  jobTemplate:
    spec:
      backoffLimit: 2
      activeDeadlineSeconds: 600
      template:
        spec:
          serviceAccountName: db-maintenance-sa
          containers:
          - name: maintenance
            image: dodatech/db-tools:1.2
            command:
            - /scripts/maintenance.sh
            env:
            - name: DB_HOST
              valueFrom:
                secretKeyRef:
                  name: db-secret
                  key: host
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: db-secret
                  key: password
            resources:
              requests:
                memory: "256Mi"
                cpu: "250m"
              limits:
                memory: "512Mi"
                cpu: "500m"
          restartPolicy: OnFailure
kubectl apply -f cronjob-maintenance.yaml
kubectl get cronjob db-maintenance -o yaml
kubectl describe cronjob db-maintenance

What's Next

Now that you understand Kubernetes CronJobs, explore systemd timers and other cron alternatives, then learn about cron monitoring best practices.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro