DEV Community

janak0ff
janak0ff

Posted on

Schedule Cronjobs in Kubernetes

The Nautilus DevOps team is setting up recurring tasks on different schedules. Currently, they're developing scripts to be executed periodically. To kickstart the process, they're creating cron jobs in the Kubernetes cluster with placeholder commands. Follow the instructions below:

  1. Create a cronjob named devops.
  2. Set Its schedule to something like */7 * * * *. You can set any schedule for now.
  3. Name the container cron-devops.
  4. Utilize the httpd image with latest tag (specify as httpd:latest).
  5. Execute the dummy command echo Welcome to xfusioncorp!.
  6. Ensure the restart policy is OnFailure.

Introduction

Imagine you need to run a backup every night at 2 AM, clean up logs every hour, or send a report every Monday morning. In traditional systems, you'd use cron jobs. In Kubernetes, you use CronJobs!

In this beginner-friendly guide, we'll walk through creating and managing CronJobs in Kubernetes. By the end, you'll be able to schedule any recurring task in your Kubernetes cluster with confidence.


What You'll Learn

  • What Kubernetes CronJobs are and why they matter
  • How to create and configure CronJobs
  • How to test CronJobs manually
  • How to monitor and troubleshoot CronJobs
  • Best practices for production use

Table of Contents

  1. What are Kubernetes CronJobs?
  2. Understanding Cron Schedule Syntax
  3. Step-by-Step CronJob Creation
  4. Testing Your CronJob
  5. Monitoring and Verification
  6. Real-World Examples
  7. Common Issues and Troubleshooting
  8. Best Practices
  9. Advanced Configuration
  10. Conclusion

What are Kubernetes CronJobs?

The Problem CronJobs Solve

In any production environment, you need to run tasks on a schedule:

  • Backups: Daily database backups
  • Cleanups: Remove old logs and temporary files
  • Reports: Generate and send daily/weekly reports
  • Sync: Synchronize data between systems
  • Health Checks: Periodic system verification

CronJobs are Kubernetes' answer to these recurring tasks.

How CronJobs Work

Think of a CronJob as a scheduler that creates Jobs at specific times:

┌─────────────────────────────────────────────┐
│              CronJob                        │
│          (Schedule: */7 * * * *)            │
│                                             │
│   ┌─────────────────────────────────────┐   │
│   │      Job (Run 1)                   │   │
│   │      Pod: Executes command          │   │
│   └─────────────────────────────────────┘   │
│   ┌─────────────────────────────────────┐   │
│   │      Job (Run 2)                   │   │
│   │      Pod: Executes command          │   │
│   └─────────────────────────────────────┘   │
│   ┌─────────────────────────────────────┐   │
│   │      Job (Run 3)                   │   │
│   │      Pod: Executes command          │   │
│   └─────────────────────────────────────┘   │
└─────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

CronJob vs Other Resources

Resource Purpose When to Use
Pod Run a single container One-time task
Job Run until completion Batch processing
CronJob Run on a schedule Recurring tasks
Deployment Run continuously Web servers, APIs

Understanding Cron Schedule Syntax

The Schedule Format

CronJobs use the standard cron schedule format:

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

Common Schedule Examples

Schedule Description Use Case
*/7 * * * * Every 7 minutes Quick tests
*/15 * * * * Every 15 minutes Log rotation
0 */2 * * * Every 2 hours Cache refresh
0 0 * * * Daily at midnight Daily backups
0 0 * * 0 Weekly on Sunday Weekly reports
0 0 1 * * Monthly on the 1st Monthly billing
0 0 1 1 * Yearly on Jan 1st Yearly tasks

Predefined Schedules

Name Schedule Description
@yearly 0 0 1 1 * Once a year
@monthly 0 0 1 * * Once a month
@weekly 0 0 * * 0 Once a week
@daily 0 0 * * * Once a day
@hourly 0 * * * * Once an hour

Note: Some Kubernetes versions support these shortcuts, but using the standard format is more portable.


Step-by-Step CronJob Creation

The Scenario

The Nautilus DevOps team needs to create a CronJob that:

  • Runs every 7 minutes
  • Uses the httpd:latest image
  • Executes: echo Welcome to xfusioncorp!
  • Has restart policy OnFailure

Step 1: Generate the YAML

First, let's create the base YAML manifest:

kubectl create cronjob devops \
  --image=httpd:latest \
  --schedule="*/7 * * * *" \
  --dry-run=client -o yaml > devops-cronjob.yaml
Enter fullscreen mode Exit fullscreen mode

This generates the YAML template for us.

Step 2: Edit the YAML

Open the file and customize it:

nano devops-cronjob.yaml
Enter fullscreen mode Exit fullscreen mode

Before (generated):

apiVersion: batch/v1
kind: CronJob
metadata:
  creationTimestamp: null
  name: devops
spec:
  schedule: "*/7 * * * *"
  jobTemplate:
    metadata:
      creationTimestamp: null
    spec:
      template:
        metadata:
          creationTimestamp: null
        spec:
          containers:
          - image: httpd:latest
            name: devops
            resources: {}
          restartPolicy: OnFailure
status: {}
Enter fullscreen mode Exit fullscreen mode

After (modified):

apiVersion: batch/v1
kind: CronJob
metadata:
  name: devops
spec:
  schedule: "*/7 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: cron-devops
            image: httpd:latest
            command: ["/bin/sh"]
            args: ["-c", "echo Welcome to xfusioncorp!"]
          restartPolicy: OnFailure
Enter fullscreen mode Exit fullscreen mode

Step 3: Apply the CronJob

Create the CronJob in your cluster:

kubectl apply -f devops-cronjob.yaml
Enter fullscreen mode Exit fullscreen mode

Expected output:

cronjob.batch/devops created
Enter fullscreen mode Exit fullscreen mode

Step 4: Verify the CronJob

Check that it was created:

kubectl get cronjobs
Enter fullscreen mode Exit fullscreen mode

Expected output:

NAME     SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
devops   */7 * * * *   False     0        <none>          10s
Enter fullscreen mode Exit fullscreen mode

Step 5: Wait for It to Run

The CronJob will run at the next 7-minute mark. You can check for jobs:

kubectl get jobs
Enter fullscreen mode Exit fullscreen mode

After the first run:

NAME                COMPLETIONS   DURATION   AGE
devops-xxxxxxxxxx   1/1           2s         15s
Enter fullscreen mode Exit fullscreen mode

Step 6: Check the Results

View the pods:

kubectl get pods
Enter fullscreen mode Exit fullscreen mode

Expected output:

NAME                         READY   STATUS      RESTARTS   AGE
devops-xxxxxxxxxx-xxxxx      0/1     Completed   0          20s
Enter fullscreen mode Exit fullscreen mode

View the logs:

kubectl logs devops-xxxxxxxxxx-xxxxx
Enter fullscreen mode Exit fullscreen mode

Expected output:

Welcome to xfusioncorp!
Enter fullscreen mode Exit fullscreen mode

Testing Your CronJob

Method 1: Create a One-Time Job

The quickest way to test a CronJob is to create a manual job from it:

kubectl create job --from=cronjob/devops devops-manual-test
Enter fullscreen mode Exit fullscreen mode

This creates a one-time job that uses the same configuration.

Method 2: Watch the CronJob

Monitor the CronJob in real-time:

kubectl get cronjob devops -w
Enter fullscreen mode Exit fullscreen mode

Method 3: Watch Jobs and Pods

In separate terminals:

# Terminal 1 - Watch CronJob
kubectl get cronjob devops -w

# Terminal 2 - Watch Jobs
kubectl get jobs -w

# Terminal 3 - Watch Pods
kubectl get pods -w
Enter fullscreen mode Exit fullscreen mode

Method 4: Change Schedule for Testing

Temporarily change the schedule to run every minute:

kubectl patch cronjob devops -p '{"spec":{"schedule":"* * * * *"}}'
Enter fullscreen mode Exit fullscreen mode

After testing, change it back:

kubectl patch cronjob devops -p '{"spec":{"schedule":"*/7 * * * *"}}'
Enter fullscreen mode Exit fullscreen mode

Monitoring and Verification

1. Check CronJob Status

# Basic status
kubectl get cronjob devops

# Detailed status
kubectl describe cronjob devops

# Check if suspended
kubectl get cronjob devops -o jsonpath='{.spec.suspend}'

# Check last schedule time
kubectl get cronjob devops -o jsonpath='{.status.lastScheduleTime}'
Enter fullscreen mode Exit fullscreen mode

2. View Jobs

# List all jobs
kubectl get jobs

# List jobs from this CronJob
kubectl get jobs -l job-name=devops

# Get job details
kubectl describe job devops-xxxxxxxxxx
Enter fullscreen mode Exit fullscreen mode

3. View Pods

# List pods
kubectl get pods

# List pods from this CronJob
kubectl get pods -l job-name=devops

# Get pod details
kubectl describe pod devops-xxxxxxxxxx-xxxxx

# View logs
kubectl logs devops-xxxxxxxxxx-xxxxx
Enter fullscreen mode Exit fullscreen mode

4. Check Events

# Events related to CronJob
kubectl get events --field-selector involvedObject.name=devops

# All events
kubectl get events --sort-by='.lastTimestamp' | tail -10
Enter fullscreen mode Exit fullscreen mode

5. Complete Monitoring Script

#!/bin/bash
echo "=== CronJob Monitor ==="
echo "Time: $(date)"
echo ""

echo "--- CronJob Status ---"
kubectl get cronjob devops
echo ""

echo "--- Jobs ---"
kubectl get jobs -l job-name=devops
echo ""

echo "--- Pods ---"
kubectl get pods -l job-name=devops
echo ""

echo "--- Latest Logs ---"
LATEST_POD=$(kubectl get pods -l job-name=devops --sort-by=.metadata.creationTimestamp -o name 2>/dev/null | tail -1 | cut -d'/' -f2)
if [ -n "$LATEST_POD" ]; then
    echo "Logs from $LATEST_POD:"
    kubectl logs $LATEST_POD 2>/dev/null || echo "No logs yet"
else
    echo "No pods found"
fi
Enter fullscreen mode Exit fullscreen mode

Real-World Examples

Example 1: Database Backup

apiVersion: batch/v1
kind: CronJob
metadata:
  name: db-backup
spec:
  schedule: "0 2 * * *"  # Daily at 2 AM
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: postgres:13
            command: ["/bin/sh"]
            args: ["-c", "pg_dump -h db-service -U postgres mydb > /backup/backup.sql"]
            env:
            - name: PGPASSWORD
              valueFrom:
                secretKeyRef:
                  name: db-secret
                  key: password
            volumeMounts:
            - name: backup-storage
              mountPath: /backup
          restartPolicy: OnFailure
          volumes:
          - name: backup-storage
            persistentVolumeClaim:
              claimName: backup-pvc
Enter fullscreen mode Exit fullscreen mode

Example 2: Log Cleanup

apiVersion: batch/v1
kind: CronJob
metadata:
  name: log-cleanup
spec:
  schedule: "0 1 * * *"  # Daily at 1 AM
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: cleanup
            image: busybox:1.35
            command: ["/bin/sh"]
            args: ["-c", "find /var/log -name '*.log' -mtime +7 -delete"]
            volumeMounts:
            - name: logs
              mountPath: /var/log
          restartPolicy: OnFailure
          volumes:
          - name: logs
            persistentVolumeClaim:
              claimName: logs-pvc
Enter fullscreen mode Exit fullscreen mode

Example 3: Health Check

apiVersion: batch/v1
kind: CronJob
metadata:
  name: health-check
spec:
  schedule: "*/10 * * * *"  # Every 10 minutes
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: health-check
            image: curlimages/curl:7.85.0
            command: ["/bin/sh"]
            args: ["-c", "curl -f http://app-service/health || exit 1"]
          restartPolicy: OnFailure
Enter fullscreen mode Exit fullscreen mode

Example 4: Report Generation

apiVersion: batch/v1
kind: CronJob
metadata:
  name: report-generator
spec:
  schedule: "0 8 * * 1"  # Monday at 8 AM
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: report
            image: python:3.9
            command: ["/bin/sh"]
            args: ["-c", "python /scripts/generate_report.py"]
            env:
            - name: REPORT_EMAIL
              value: "admin@company.com"
            volumeMounts:
            - name: scripts
              mountPath: /scripts
            - name: reports
              mountPath: /reports
          restartPolicy: OnFailure
          volumes:
          - name: scripts
            configMap:
              name: report-scripts
          - name: reports
            persistentVolumeClaim:
              claimName: reports-pvc
Enter fullscreen mode Exit fullscreen mode

Common Issues and Troubleshooting

Issue 1: CronJob Not Creating Jobs

Symptoms: CronJob exists but no jobs appear

Solutions:

# Check if suspended
kubectl get cronjob devops -o jsonpath='{.spec.suspend}'

# Resume if suspended
kubectl patch cronjob devops -p '{"spec":{"suspend":false}}'

# Check events
kubectl describe cronjob devops

# Check if schedule is valid
kubectl get cronjob devops -o jsonpath='{.spec.schedule}'
Enter fullscreen mode Exit fullscreen mode

Issue 2: Jobs Failing

Symptoms: Jobs show status Failed

Solutions:

# Describe the job
kubectl describe job devops-xxxxxxxxxx

# Check pod logs
kubectl logs devops-xxxxxxxxxx-xxxxx

# Check if command exists in the image
kubectl run test --image=httpd:latest --rm -it -- /bin/sh -c "echo test"

# Test the command manually
docker run --rm httpd:latest /bin/sh -c "echo Welcome to xfusioncorp!"
Enter fullscreen mode Exit fullscreen mode

Issue 3: Command Not Found

Error: /bin/sh: command not found

Solutions:

# Check the image
kubectl get cronjob devops -o jsonpath='{.spec.jobTemplate.spec.template.spec.containers[0].image}'

# Use correct shell
kubectl patch cronjob devops -p '{"spec":{"jobTemplate":{"spec":{"template":{"spec":{"containers":[{"name":"cron-devops","command":["/bin/bash","-c","echo Welcome to xfusioncorp!"]}]}}}}}}'
Enter fullscreen mode Exit fullscreen mode

Issue 4: Too Many Failed Jobs

Symptoms: Many failed jobs accumulating

Solutions:

# Set failed jobs history limit
kubectl patch cronjob devops -p '{"spec":{"failedJobsHistoryLimit":1}}'

# Clean up old jobs
kubectl delete jobs -l job-name=devops
Enter fullscreen mode Exit fullscreen mode

Issue 5: Jobs Not Completing

Symptoms: Jobs stuck in Running state

Solutions:

# Check if command hangs
kubectl logs devops-xxxxxxxxxx-xxxxx

# Check pod status
kubectl describe pod devops-xxxxxxxxxx-xxxxx

# Set a deadline
kubectl patch cronjob devops -p '{"spec":{"jobTemplate":{"spec":{"activeDeadlineSeconds":60}}}}'
Enter fullscreen mode Exit fullscreen mode

Best Practices

✅ DO's

1. Use Specific Image Tags

# BAD - Unpredictable
image: httpd:latest

# GOOD - Specific version
image: httpd:2.4.54
Enter fullscreen mode Exit fullscreen mode

2. Set Resource Limits

resources:
  requests:
    memory: "32Mi"
    cpu: "50m"
  limits:
    memory: "64Mi"
    cpu: "100m"
Enter fullscreen mode Exit fullscreen mode

3. Set Appropriate History Limits

successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
Enter fullscreen mode Exit fullscreen mode

4. Add Proper Labels

metadata:
  labels:
    app: devops
    type: cronjob
    environment: production
Enter fullscreen mode Exit fullscreen mode

5. Set Starting Deadline

startingDeadlineSeconds: 200
Enter fullscreen mode Exit fullscreen mode

6. Use Concurrency Policies

concurrencyPolicy: Forbid  # Don't run concurrent jobs
Enter fullscreen mode Exit fullscreen mode

7. Test Commands Locally

# Test before deploying
docker run --rm httpd:latest /bin/sh -c "echo Welcome to xfusioncorp!"
Enter fullscreen mode Exit fullscreen mode

8. Monitor Your CronJobs

# Set up alerts for failures
# Monitor job completion status
# Check logs regularly
Enter fullscreen mode Exit fullscreen mode

❌ DON'Ts

1. Don't Use :latest in Production

  • Can cause unexpected behavior
  • Harder to debug

2. Don't Set Too Frequent Schedules

  • Consider system load
  • Use appropriate intervals

3. Don't Ignore Failed Jobs

  • Investigate failures
  • Set up monitoring

4. Don't Delete CronJob Without Cleaning Up

  • Orphaned jobs may remain
  • Clean up old jobs

5. Don't Use CronJobs for Long-Running Tasks

  • CronJobs are for batch jobs
  • Use Deployments for long-running services

Advanced Configuration

1. Concurrency Policy

spec:
  concurrencyPolicy: Allow      # Default - allow concurrent runs
  # concurrencyPolicy: Forbid   # Don't allow concurrent runs
  # concurrencyPolicy: Replace  # Replace old job with new one
Enter fullscreen mode Exit fullscreen mode

2. Starting Deadline

spec:
  startingDeadlineSeconds: 200  # If job doesn't start within 200s, skip it
Enter fullscreen mode Exit fullscreen mode

3. History Limits

spec:
  successfulJobsHistoryLimit: 5  # Keep last 5 successful jobs
  failedJobsHistoryLimit: 2      # Keep last 2 failed jobs
Enter fullscreen mode Exit fullscreen mode

4. Suspend/Resume

spec:
  suspend: true  # Pause the CronJob (False = active)
Enter fullscreen mode Exit fullscreen mode

5. Complete Production Example

apiVersion: batch/v1
kind: CronJob
metadata:
  name: devops
  namespace: production
  labels:
    app: devops
    type: cronjob
    environment: production
    team: nautilus
spec:
  schedule: "*/7 * * * *"
  concurrencyPolicy: Forbid
  startingDeadlineSeconds: 300
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 1
  suspend: false
  jobTemplate:
    spec:
      parallelism: 1
      completions: 1
      backoffLimit: 3
      activeDeadlineSeconds: 60
      template:
        metadata:
          labels:
            app: devops-cronjob
            run: devops
        spec:
          containers:
          - name: cron-devops
            image: httpd:2.4.54
            command: ["/bin/sh"]
            args: ["-c", "echo Welcome to xfusioncorp!"]
            resources:
              requests:
                memory: "32Mi"
                cpu: "50m"
              limits:
                memory: "64Mi"
                cpu: "100m"
          restartPolicy: OnFailure
          imagePullPolicy: IfNotPresent
Enter fullscreen mode Exit fullscreen mode

CronJob Management Commands

Create CronJob

kubectl create cronjob NAME --image=IMAGE --schedule=SCHEDULE
Enter fullscreen mode Exit fullscreen mode

List CronJobs

kubectl get cronjobs
kubectl get cronjobs --all-namespaces
Enter fullscreen mode Exit fullscreen mode

View Details

kubectl describe cronjob NAME
kubectl get cronjob NAME -o yaml
Enter fullscreen mode Exit fullscreen mode

Update CronJob

# Update schedule
kubectl patch cronjob NAME -p '{"spec":{"schedule":"NEW_SCHEDULE"}}'

# Update image
kubectl set image cronjob NAME CONTAINER=IMAGE

# Update command
kubectl patch cronjob NAME -p '{"spec":{"jobTemplate":{"spec":{"template":{"spec":{"containers":[{"name":"CONTAINER","command":["NEW","COMMAND"]}]}}}}}}'
Enter fullscreen mode Exit fullscreen mode

Suspend/Resume

# Suspend
kubectl patch cronjob NAME -p '{"spec":{"suspend":true}}'

# Resume
kubectl patch cronjob NAME -p '{"spec":{"suspend":false}}'
Enter fullscreen mode Exit fullscreen mode

Manual Job Creation

kubectl create job --from=cronjob/NAME JOB_NAME
Enter fullscreen mode Exit fullscreen mode

Delete CronJob

kubectl delete cronjob NAME
kubectl delete -f cronjob.yaml
Enter fullscreen mode Exit fullscreen mode

Conclusion

You've now learned how to schedule recurring tasks in Kubernetes using CronJobs! This is a powerful feature that allows you to automate routine operations in your cluster.

Key Takeaways

  1. CronJobs are for scheduled tasks - Backups, cleanups, reports, etc.
  2. Use standard cron syntax - */7 * * * * means every 7 minutes
  3. Test with manual jobs - Create one-time jobs from CronJobs
  4. Monitor your CronJobs - Check status, logs, and events
  5. Follow best practices - Use specific image tags, set resource limits
  6. Handle failures gracefully - Set appropriate backoff limits and history

What You Learned

✅ What CronJobs are and when to use them
✅ How to read and write cron schedules
✅ How to create and configure CronJobs
✅ How to test and monitor CronJobs
✅ Common issues and troubleshooting
✅ Best practices for production

Top comments (0)