Job vs CronJob
Key Differences in Kubernetes
A Job runs a task to completion — it creates one or more Pods and ensures they finish successfully. A CronJob creates Jobs on a recurring schedule defined by a cron expression. Use a Job for one-time tasks like database migrations; use a CronJob for recurring tasks like nightly backups or periodic cleanup.
Side-by-Side Comparison
| Dimension | Job | CronJob |
|---|---|---|
| Execution | Runs once when created | Runs on a recurring schedule defined by a cron expression |
| Trigger | Triggered by creation of the Job resource | Triggered automatically by the CronJob controller at scheduled times |
| Relationship | Standalone resource or created by a CronJob | Higher-level resource that creates and manages Jobs |
| Completion Tracking | Tracks completions and retries directly | Delegates completion tracking to the created Jobs |
| Concurrency | parallelism and completions control parallel execution | concurrencyPolicy controls whether concurrent Jobs are allowed |
| History | Remains until manually deleted or TTL expires | Retains configurable number of successful/failed Job history |
| Failure Handling | backoffLimit controls retry attempts | Inherits backoffLimit from Job template; also has startingDeadlineSeconds |
Detailed Breakdown
Job — Run to Completion
A Job creates one or more Pods and ensures a specified number of them complete successfully. Unlike a Deployment (which keeps Pods running forever), a Job's Pods terminate once their task is done.
apiVersion: batch/v1
kind: Job
metadata:
name: db-migration
spec:
backoffLimit: 3
activeDeadlineSeconds: 600
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: my-app:1.0.0
command: ["python", "manage.py", "migrate"]
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: url
Key fields:
backoffLimit: 3— retry up to 3 times on failureactiveDeadlineSeconds: 600— kill the Job if it runs longer than 10 minutesrestartPolicy: Never— do not restart the container; create a new Pod instead (alternative:OnFailureto restart in-place)
Parallel Jobs
Jobs support parallel execution for batch workloads:
apiVersion: batch/v1
kind: Job
metadata:
name: batch-processor
spec:
completions: 10
parallelism: 3
backoffLimit: 5
template:
spec:
restartPolicy: OnFailure
containers:
- name: processor
image: batch-worker:1.0.0
This runs 10 total completions with up to 3 Pods running in parallel at a time. Each Pod processes one unit of work. The Job is complete when 10 Pods have finished successfully.
CronJob — Scheduled Jobs
A CronJob creates Job objects on a cron schedule:
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-backup
spec:
schedule: "0 2 * * *" # Every day at 2:00 AM
timeZone: "America/New_York"
concurrencyPolicy: Forbid
startingDeadlineSeconds: 300
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 5
jobTemplate:
spec:
backoffLimit: 2
activeDeadlineSeconds: 3600
template:
spec:
restartPolicy: OnFailure
containers:
- name: backup
image: backup-tool:1.0.0
command: ["./backup.sh"]
env:
- name: S3_BUCKET
value: "my-backups"
The cron schedule follows standard cron syntax: minute, hour, day-of-month, month, day-of-week. Common examples:
*/5 * * * *— every 5 minutes0 */6 * * *— every 6 hours0 2 * * *— daily at 2 AM0 0 * * 0— weekly on Sunday at midnight0 0 1 * *— first day of every month at midnight
Concurrency Policy
The concurrencyPolicy field is critical for CronJobs that might overlap:
spec:
concurrencyPolicy: Allow # Default — multiple Jobs can run at the same time
spec:
concurrencyPolicy: Forbid # Skip the new Job if the previous one is still running
spec:
concurrencyPolicy: Replace # Cancel the running Job and start a new one
For backups, Forbid is usually the right choice — you do not want two backup processes running concurrently. For idempotent tasks like sending reminders, Allow is fine. For data processing where only the latest run matters, Replace ensures you are always working on fresh data.
Job History Management
CronJobs create a new Job object on each schedule trigger. Without cleanup, these accumulate:
spec:
successfulJobsHistoryLimit: 3 # Keep last 3 successful Jobs
failedJobsHistoryLimit: 5 # Keep last 5 failed Jobs
Old Jobs (and their Pods) are automatically deleted. This prevents the namespace from filling up with completed Job objects.
For standalone Jobs (not created by a CronJob), you can use the TTL controller:
apiVersion: batch/v1
kind: Job
metadata:
name: one-time-task
spec:
ttlSecondsAfterFinished: 3600 # Delete Job 1 hour after completion
template:
spec:
restartPolicy: Never
containers:
- name: task
image: task-runner:1.0.0
Missed Schedules and Deadlines
If the CronJob controller is down (or the cluster is unavailable) when a schedule fires, the Job is missed. The startingDeadlineSeconds field controls how late a Job can start:
spec:
startingDeadlineSeconds: 300 # Allow up to 5 minutes late
If more than 100 schedules are missed within the startingDeadlineSeconds window, the CronJob stops trying and logs an error. This prevents a flood of Jobs when the controller recovers after extended downtime.
Monitoring and Debugging
# Check Job status
kubectl get jobs
kubectl describe job db-migration
# View Pods created by a Job
kubectl get pods --selector=job-name=db-migration
# Check CronJob schedule and last run
kubectl get cronjobs
kubectl describe cronjob nightly-backup
# View logs from the most recent Job Pod
kubectl logs job/db-migration
# Manually trigger a CronJob for testing
kubectl create job --from=cronjob/nightly-backup manual-backup-test
The kubectl create job --from=cronjob command is particularly useful for testing — it creates a one-off Job using the CronJob's template without waiting for the next scheduled run.
Failure Handling Strategies
For critical Jobs, combine multiple failure handling mechanisms:
apiVersion: batch/v1
kind: Job
spec:
backoffLimit: 3 # Retry 3 times
activeDeadlineSeconds: 1800 # Total time limit: 30 minutes
template:
spec:
restartPolicy: OnFailure
activeDeadlineSeconds: 600 # Per-Pod time limit: 10 minutes
containers:
- name: task
image: critical-task:1.0.0
livenessProbe:
exec:
command: ["cat", "/tmp/healthy"]
periodSeconds: 30
This ensures the Job retries on failure, has a hard time limit, and the container is health-checked during execution.
Use Job when...
- •Running a one-time database migration or schema update
- •Processing a batch of data that needs to run to completion
- •Running integration tests or one-off scripts in the cluster
- •Performing a parallel computation with multiple completions
Use CronJob when...
- •Scheduling nightly database backups
- •Running periodic cleanup of expired data or temp files
- •Sending scheduled reports or notifications
- •Performing regular health checks or compliance scans
- •Rotating logs or certificates on a schedule
Model Interview Answer
“A Job is a Kubernetes resource that runs a task to completion — it creates Pods, ensures they succeed, and retries on failure up to a backoffLimit. Once all Pods complete successfully, the Job is done. A CronJob builds on top of Jobs by creating them on a cron schedule, like running a backup every night at 2 AM. The CronJob manages history by keeping a configurable number of successful and failed Jobs. Key settings include concurrencyPolicy (Allow, Forbid, Replace) which controls what happens when the next schedule fires while a previous Job is still running, and startingDeadlineSeconds which sets how late a Job can start if it misses its window.”