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:
- Create a cronjob named
devops. - Set Its schedule to something like
*/7 * * * *. You can set any schedule for now. - Name the container
cron-devops. - Utilize the
httpdimage withlatest tag(specify ashttpd:latest). - Execute the dummy command
echo Welcome to xfusioncorp!. - 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
- What are Kubernetes CronJobs?
- Understanding Cron Schedule Syntax
- Step-by-Step CronJob Creation
- Testing Your CronJob
- Monitoring and Verification
- Real-World Examples
- Common Issues and Troubleshooting
- Best Practices
- Advanced Configuration
- 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 │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
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)
│ │ │ │ │
│ │ │ │ │
│ │ │ │ │
* * * * *
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:latestimage - 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
This generates the YAML template for us.
Step 2: Edit the YAML
Open the file and customize it:
nano devops-cronjob.yaml
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: {}
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
Step 3: Apply the CronJob
Create the CronJob in your cluster:
kubectl apply -f devops-cronjob.yaml
Expected output:
cronjob.batch/devops created
Step 4: Verify the CronJob
Check that it was created:
kubectl get cronjobs
Expected output:
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
devops */7 * * * * False 0 <none> 10s
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
After the first run:
NAME COMPLETIONS DURATION AGE
devops-xxxxxxxxxx 1/1 2s 15s
Step 6: Check the Results
View the pods:
kubectl get pods
Expected output:
NAME READY STATUS RESTARTS AGE
devops-xxxxxxxxxx-xxxxx 0/1 Completed 0 20s
View the logs:
kubectl logs devops-xxxxxxxxxx-xxxxx
Expected output:
Welcome to xfusioncorp!
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
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
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
Method 4: Change Schedule for Testing
Temporarily change the schedule to run every minute:
kubectl patch cronjob devops -p '{"spec":{"schedule":"* * * * *"}}'
After testing, change it back:
kubectl patch cronjob devops -p '{"spec":{"schedule":"*/7 * * * *"}}'
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}'
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
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
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
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
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
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
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
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
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}'
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!"
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!"]}]}}}}}}'
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
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}}}}'
Best Practices
✅ DO's
1. Use Specific Image Tags
# BAD - Unpredictable
image: httpd:latest
# GOOD - Specific version
image: httpd:2.4.54
2. Set Resource Limits
resources:
requests:
memory: "32Mi"
cpu: "50m"
limits:
memory: "64Mi"
cpu: "100m"
3. Set Appropriate History Limits
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
4. Add Proper Labels
metadata:
labels:
app: devops
type: cronjob
environment: production
5. Set Starting Deadline
startingDeadlineSeconds: 200
6. Use Concurrency Policies
concurrencyPolicy: Forbid # Don't run concurrent jobs
7. Test Commands Locally
# Test before deploying
docker run --rm httpd:latest /bin/sh -c "echo Welcome to xfusioncorp!"
8. Monitor Your CronJobs
# Set up alerts for failures
# Monitor job completion status
# Check logs regularly
❌ 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
2. Starting Deadline
spec:
startingDeadlineSeconds: 200 # If job doesn't start within 200s, skip it
3. History Limits
spec:
successfulJobsHistoryLimit: 5 # Keep last 5 successful jobs
failedJobsHistoryLimit: 2 # Keep last 2 failed jobs
4. Suspend/Resume
spec:
suspend: true # Pause the CronJob (False = active)
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
CronJob Management Commands
Create CronJob
kubectl create cronjob NAME --image=IMAGE --schedule=SCHEDULE
List CronJobs
kubectl get cronjobs
kubectl get cronjobs --all-namespaces
View Details
kubectl describe cronjob NAME
kubectl get cronjob NAME -o yaml
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"]}]}}}}}}'
Suspend/Resume
# Suspend
kubectl patch cronjob NAME -p '{"spec":{"suspend":true}}'
# Resume
kubectl patch cronjob NAME -p '{"spec":{"suspend":false}}'
Manual Job Creation
kubectl create job --from=cronjob/NAME JOB_NAME
Delete CronJob
kubectl delete cronjob NAME
kubectl delete -f cronjob.yaml
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
- CronJobs are for scheduled tasks - Backups, cleanups, reports, etc.
-
Use standard cron syntax -
*/7 * * * *means every 7 minutes - Test with manual jobs - Create one-time jobs from CronJobs
- Monitor your CronJobs - Check status, logs, and events
- Follow best practices - Use specific image tags, set resource limits
- 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)