Kubernetes CronJobs bring the power of Linux cron scheduling to containerized environments. They're the go-to solution for running batch jobs, database backups, report generation, and other periodic tasks in your cluster.

This guide covers everything from basic cron job examples to advanced configuration options for production Kubernetes environments.

What is a Kubernetes CronJob?

A CronJob creates Kubernetes Jobs on a repeating schedule. Each Job spawns one or more Pods to run your task, and Kubernetes handles the lifecycle management including retries, cleanup, and concurrency control.

Think of it as Linux cronjobs, but for containers:

  • Uses the same cron expression syntax as Linux crontab
  • Runs in isolated containers with defined resources
  • Includes built-in history management and failure handling

Basic CronJob Structure

Here's a minimal Kubernetes CronJob example:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello-cron
spec:
  schedule: "*/5 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            command:
            - /bin/sh
            - -c
            - echo "Hello from Kubernetes CronJob at $(date)"
          restartPolicy: OnFailure

This CronJob runs every 5 minutes, printing a timestamped message.

Cron Expression Syntax

Kubernetes CronJobs use the standard 5-field cron format identical to Linux cronjobs:

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

Use our Cron Generator to build and validate your schedules before deploying.

Common Cron Job Examples for Kubernetes

Expression Schedule
*/5 * * * * Every 5 minutes
0 * * * * Every hour
0 0 * * * Daily at midnight
0 9 * * 1-5 Weekdays at 9 AM
0 2 * * * Daily at 2 AM
0 0 1 * * First day of month

Real-World Kubernetes CronJob Examples

Daily Database Backup

apiVersion: batch/v1
kind: CronJob
metadata:
  name: db-backup
spec:
  schedule: "0 2 * * *"
  concurrencyPolicy: Forbid
  successfulJobsHistoryLimit: 7
  failedJobsHistoryLimit: 3
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: postgres:15
            command:
            - /bin/sh
            - -c
            - |
              pg_dump $DATABASE_URL > /backup/db-$(date +%Y%m%d-%H%M%S).sql
              echo "Backup completed at $(date)"
            envFrom:
            - secretRef:
                name: database-credentials
            volumeMounts:
            - name: backup-storage
              mountPath: /backup
            resources:
              requests:
                memory: "256Mi"
                cpu: "100m"
              limits:
                memory: "512Mi"
                cpu: "500m"
          volumes:
          - name: backup-storage
            persistentVolumeClaim:
              claimName: backup-pvc
          restartPolicy: OnFailure

This cron job example:

  • Runs at 2 AM daily
  • Prevents concurrent runs with concurrencyPolicy: Forbid
  • Keeps 7 successful and 3 failed job records
  • Sets resource limits to prevent runaway containers

Weekly Cleanup Job

apiVersion: batch/v1
kind: CronJob
metadata:
  name: cleanup-old-files
spec:
  schedule: "0 3 * * 0"
  jobTemplate:
    spec:
      activeDeadlineSeconds: 3600
      template:
        spec:
          containers:
          - name: cleanup
            image: alpine
            command:
            - /bin/sh
            - -c
            - |
              echo "Starting cleanup at $(date)"
              find /data -type f -mtime +30 -delete
              echo "Cleanup completed at $(date)"
            volumeMounts:
            - name: data-volume
              mountPath: /data
          volumes:
          - name: data-volume
            persistentVolumeClaim:
              claimName: app-data-pvc
          restartPolicy: OnFailure

Runs every Sunday at 3 AM, deleting files older than 30 days.

Hourly Health Check with Notification

apiVersion: batch/v1
kind: CronJob
metadata:
  name: health-check
spec:
  schedule: "0 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: checker
            image: curlimages/curl:latest
            command:
            - /bin/sh
            - -c
            - |
              if curl -sf https://api.example.com/health; then
                echo "Health check passed"
                curl -X POST https://hooks.slack.com/services/xxx -d '{"text":"✅ API healthy"}'
              else
                echo "Health check failed!"
                curl -X POST https://hooks.slack.com/services/xxx -d '{"text":"❌ API down!"}'
                exit 1
              fi
          restartPolicy: Never

Important Configuration Options

Concurrency Policy

Control what happens when a new Job is scheduled while the previous one is still running:

spec:
  concurrencyPolicy: Forbid  # Skip if previous still running
  # concurrencyPolicy: Allow   # Run concurrent jobs (default)
  # concurrencyPolicy: Replace # Stop old job, start new one

Recommendation: Use Forbid for most cron jobs to prevent resource exhaustion and duplicate processing.

History Limits

Control how many completed and failed Jobs to retain:

spec:
  successfulJobsHistoryLimit: 3  # Keep last 3 successful jobs
  failedJobsHistoryLimit: 1       # Keep last 1 failed job

Lower limits save cluster resources; higher limits aid debugging.

Starting Deadline

If a Job misses its scheduled time (cluster downtime, etc.), how long to wait before giving up:

spec:
  startingDeadlineSeconds: 300  # 5 minutes

Without this, Kubernetes may try to "catch up" on all missed runs.

Active Deadline

Maximum time a Job can run before being terminated:

spec:
  jobTemplate:
    spec:
      activeDeadlineSeconds: 3600  # 1 hour max

Essential for preventing runaway containers.

Suspend

Temporarily pause the CronJob without deleting it:

spec:
  suspend: true

Useful during maintenance or debugging.

Troubleshooting Kubernetes CronJobs

CronJob Not Running

1. Check if CronJob is suspended:

kubectl get cronjob my-cronjob -o jsonpath='{.spec.suspend}'

2. Verify the cron expression is correct:

Use our Cron Expression Translator to validate your schedule.

3. Check for quota issues:

kubectl describe cronjob my-cronjob

Look for events mentioning quota exceeded or resource limits.

4. Check the kube-controller-manager timezone:

CronJobs use the timezone of the Kubernetes controller manager, not your local timezone. Most clusters run in UTC.

Viewing Job Logs

# List jobs created by a CronJob
kubectl get jobs -l job-name=my-cronjob

# Get logs from the most recent job
kubectl logs job/my-cronjob-1234567890

# Follow logs in real-time
kubectl logs -f job/my-cronjob-1234567890

Checking Job History

# List all jobs sorted by creation time
kubectl get jobs --sort-by=.metadata.creationTimestamp

# Get details on failed jobs
kubectl describe job my-cronjob-1234567890

Kubernetes CronJob Best Practices

  1. Set resource limits - Prevent runaway containers from consuming cluster resources

  2. Use appropriate restartPolicy:

    • OnFailure - Retries in the same Pod (good for transient failures)
    • Never - Creates new Pods for retries (good for debugging)
  3. Configure deadlines - Set activeDeadlineSeconds to prevent infinite loops

  4. Monitor execution - Use Prometheus metrics or send heartbeats to monitoring services like Healthchecks.io

  5. Handle time zones - Remember that CronJobs use the kube-controller-manager's timezone (usually UTC)

  6. Use Forbid concurrency - Prevent overlapping job runs for most use cases

  7. Keep history limits low - Reduce cluster resource consumption

  8. Test expressions before deploying - Use our Cron Generator to validate

Essential kubectl Commands for CronJobs

Command Description
kubectl get cronjobs List all CronJobs
kubectl describe cronjob NAME Detailed CronJob info
kubectl get jobs List all Jobs (including from CronJobs)
kubectl logs job/NAME View Job logs
kubectl delete cronjob NAME Delete CronJob
kubectl create job --from=cronjob/NAME test-job Manually trigger a CronJob

Migrating from Linux Cronjobs

If you're moving from traditional Linux cron to Kubernetes:

Linux Crontab Kubernetes CronJob
crontab -e kubectl edit cronjob
crontab -l kubectl get cronjobs
grep CRON /var/log/syslog kubectl logs job/NAME
Script on filesystem Container image
Environment in crontab ConfigMaps/Secrets

The cron expression syntax is identical any expression that works in Linux crontab works in Kubernetes.

Conclusion

Kubernetes CronJobs provide a robust, containerized way to run scheduled tasks. With proper configuration for concurrency, history limits, and deadlines, they can reliably handle your periodic workloads at scale.

Key takeaways:

  • Same cron syntax as Linux cronjobs
  • Use concurrencyPolicy: Forbid to prevent overlaps
  • Set activeDeadlineSeconds to limit runtime
  • Monitor with health check endpoints
  • Remember timezones (usually UTC)

Need help creating cron schedules? Use our Cron Generator to build, validate, and test expressions before deploying to your cluster!