Kubernetes Jobs and CronJobs
Learn Kubernetes Jobs and CronJobs with plain-English explanations and real examples..
20+ years shipping production infrastructure and CI/CD at scale. Drawn from code that ran under real load.
- ✓Kubernetes cluster (v1.27+ for timeZone support), kubectl installed, basic understanding of YAML and pod lifecycle, familiarity with cron syntax.
Think of Kubernetes Jobs and CronJobs like a tool in your DevOps toolkit — once you understand what it does and when to reach for it, managing Kubernetes clusters becomes second nature.
You've got a pod that runs a database migration. It finishes in 30 seconds, but your deployment keeps restarting it, creating a loop of failed migrations. That's the moment you realize Kubernetes needs a controller for finite work. Jobs and CronJobs are that controller. They manage pods that are meant to exit, handling retries, parallelism, and scheduling. Without them, you're hacking together init containers or one-shot deployments that never truly complete. In production, a misconfigured Job can silently fail, leaving your data in an inconsistent state. Or a CronJob that runs every minute can overwhelm your database. This isn't theory—it's the difference between a reliable batch pipeline and a pager at 3 AM.
What Are Kubernetes Jobs?
A Kubernetes Job creates one or more pods and ensures that a specified number of them successfully terminate. Unlike Deployments or StatefulSets, which aim to keep pods running indefinitely, Jobs are designed for finite tasks. When a pod completes (its main process exits with code 0), the Job marks it as successful. If the pod fails (non-zero exit), the Job can retry according to its backoff limit. Jobs are ideal for batch processing, data exports, or any task that should run once and stop. They handle pod failures, node failures, and even allow parallel execution. The key is that a Job is considered complete only when the desired number of successful pod completions is reached. This makes them reliable for one-off tasks that must succeed.
restartPolicy: Never or OnFailure. The default Always is invalid for Jobs and will cause an error.backoffLimit to avoid infinite retries on a buggy job that could exhaust cluster resources.Job Patterns: Parallelism and Completions
Jobs can run multiple pods in parallel or sequentially. The spec.completions field defines how many successful pod completions are needed. The spec.parallelism field defines how many pods can run concurrently. For example, a job with completions=10 and parallelism=3 will run up to 3 pods at a time until 10 total successes. This is useful for processing a queue of work items. You can also set parallelism without completions to run a fixed number of pods in parallel, each doing independent work. The Job controller manages the lifecycle, creating new pods as others complete. Be careful with parallelism: too high can overwhelm downstream services, too low can waste time. In production, tune these values based on your workload's resource profile and external dependencies.
JOB_COMPLETION_INDEX (0-based) to assign unique work items. This is great for sharded processing.parallelism and total work with completions.Handling Job Failures: Backoff and TTL
Jobs fail when pods exit with non-zero status. The backoffLimit controls retries: after a failure, the Job waits an exponentially increasing amount of time before retrying (10s, 20s, 40s... up to 6 minutes). Once the limit is reached, the Job is marked as Failed. You can also set activeDeadlineSeconds to cap the total runtime. For cleanup, use ttlSecondsAfterFinished to automatically delete completed or failed Jobs after a delay. Without this, completed Jobs linger indefinitely, cluttering your cluster. In production, always set ttlSecondsAfterFinished to avoid resource leaks. Also, monitor failed Jobs with alerts—silent failures can corrupt data.
backoffLimit is not set, it defaults to 6. For critical jobs, set a reasonable limit to prevent runaway resource consumption.activeDeadlineSeconds to prevent jobs from running forever due to a deadlock or external dependency hang.CronJobs: Scheduled Jobs in Kubernetes
A CronJob creates Jobs on a recurring schedule defined by a cron expression. It's the Kubernetes equivalent of cron on Linux. CronJobs are perfect for periodic tasks like backups, report generation, or cleanup. The schedule is in standard cron format with five fields (minute, hour, day of month, month, day of week). Kubernetes also supports time zones via spec.timeZone (beta in 1.27+). A CronJob creates a Job object for each scheduled run. If a Job is still running when the next schedule fires, the CronJob can skip or allow concurrency based on concurrencyPolicy. In production, always set startingDeadlineSeconds to handle missed schedules due to cluster downtime.
spec.timeZone is available in Kubernetes 1.27+ (beta). For older versions, use UTC and convert in your application.concurrencyPolicy: Forbid for jobs that must not overlap, like database backups, to avoid corruption.CronJob Concurrency and Missed Schedules
The concurrencyPolicy controls what happens when a new Job is due while a previous one is still running. Options: Allow (default) runs concurrently, Forbid skips the new run, Replace cancels the old run and starts new. Use Forbid for idempotent tasks that must not overlap. Replace is useful for long-running jobs where only the latest state matters, like cache refreshes. The startingDeadlineSeconds sets a deadline for starting a Job if the CronJob controller missed the schedule (e.g., due to downtime). If the deadline passes, the run is considered failed. In production, monitor missed schedules with alerts—they can indicate controller issues or resource constraints.
Replace carefully—if the old job is writing to a file, replacing it mid-write may leave partial data.startingDeadlineSeconds to at least the expected runtime to avoid false failures during brief outages.Job and CronJob Best Practices for Production
Production Jobs need careful configuration. Always set resource requests and limits to prevent noisy neighbor issues. Use activeDeadlineSeconds to cap runtime. Set backoffLimit to a reasonable number (e.g., 3-5) to avoid infinite retries. For CronJobs, set startingDeadlineSeconds to handle controller delays. Use ttlSecondsAfterFinished to auto-cleanup completed Jobs. Monitor Job status with metrics (e.g., Prometheus) and alert on failures. Consider using spec.jobTemplate.spec.template.spec.restartPolicy: Never to avoid pod restarts that mask failures. For idempotent jobs, ensure they can be safely retried. Finally, test your Jobs locally with kubectl create job --from=cronjob/... to validate the template.
kubectl create job --from=cronjob/my-cronjob manual-test to test a CronJob template without waiting for the schedule.Monitoring and Debugging Jobs
Debugging Jobs requires different tools than Deployments. Use kubectl describe job <name> to see events and conditions. kubectl logs job/<name> shows logs from the last pod. For parallel jobs, use kubectl logs -l job-name=<name> to get logs from all pods. Check pod status with kubectl get pods --selector=job-name=<name>. If a Job is stuck, check for resource constraints, image pull errors, or pod evictions. Use kubectl get events --field-selector involvedObject.kind=Job to see Job-specific events. In production, set up Prometheus alerts for Job failures and duration anomalies. Also, consider using a sidecar container to export metrics from batch jobs.
Complete or Failed. Use kubectl wait --for=condition=complete job/myjob to block until completion.kube_job_status_failed > 0 to catch job failures immediately.Advanced Patterns: Work Queues and Fan-Out
Jobs can implement work queue patterns where a coordinator pod distributes work to worker pods. One common pattern is to use a Redis queue: a Job creates multiple worker pods that pull tasks from the queue. The Job's completions field can be left unset (defaults to 1) if each worker processes multiple tasks. Alternatively, use spec.completions to match the number of queue items. For fan-out, create a Job per task using a parent Job that spawns child Jobs via the Kubernetes API. This is powerful but complex—consider using a workflow engine like Argo or Tekton for complex DAGs. In production, ensure your queue is resilient and that workers handle duplicate tasks idempotently.
CronJob Gotchas: Time Zones and Daylight Saving
CronJobs in Kubernetes use UTC by default. If your schedule must run at a local time, you have two options: use spec.timeZone (K8s 1.27+) or convert to UTC in your cron expression. Time zone support handles daylight saving transitions correctly—if a time is skipped (spring forward), the job runs at the next valid time; if repeated (fall back), it runs only once. Without time zone support, you must manually adjust schedules twice a year, which is error-prone. In production, always use timeZone if available. Also, be aware that cron expressions with @daily or @weekly are syntactic sugar but less flexible.
for any, /5 for every 5, comma for lists.Security: ServiceAccounts and RBAC for Jobs
Jobs often need access to Kubernetes API or external services. Use a dedicated ServiceAccount with minimal RBAC permissions. Never use the default ServiceAccount in the namespace—it may have unintended permissions. Create a ServiceAccount and bind it to a Role with only the necessary verbs (get, list, create, etc.) on specific resources. For Jobs that need to create other Jobs (e.g., fan-out pattern), grant permissions to create Jobs in the same namespace. Also, consider using Pod Security Standards to restrict privileged containers. In production, audit Job permissions regularly—overly permissive roles are a common security hole.
kubectl auth can-i or automated scanners to detect over-permissioned accounts.Alternatives to Jobs: When Not to Use Them
Jobs are not always the right tool. For long-running services, use Deployments or StatefulSets. For one-off tasks that need to run on every node, use DaemonSets. For complex workflows with dependencies, consider workflow engines like Argo Workflows or Tekton. For serverless batch processing, consider Knative or Google Cloud Run. Jobs are best for simple, finite tasks that need retries and parallelism. If your task requires stateful storage, use StatefulSets with volumeClaimTemplates. If you need to run a task on a schedule but the task is very short-lived, a CronJob is fine, but consider using a lightweight alternative like a Kubernetes operator if you need more control.
| File | Command / Code | Purpose |
|---|---|---|
| simple-job.yaml | apiVersion: batch/v1 | What Are Kubernetes Jobs? |
| parallel-job.yaml | apiVersion: batch/v1 | Job Patterns |
| job-with-retries.yaml | apiVersion: batch/v1 | Handling Job Failures |
| cronjob.yaml | apiVersion: batch/v1 | CronJobs |
| cronjob-concurrency.yaml | apiVersion: batch/v1 | CronJob Concurrency and Missed Schedules |
| production-job.yaml | apiVersion: batch/v1 | Job and CronJob Best Practices for Production |
| debug-commands.sh | kubectl describe job pi-job | Monitoring and Debugging Jobs |
| work-queue-job.yaml | apiVersion: batch/v1 | Advanced Patterns |
| cronjob-timezone.yaml | apiVersion: batch/v1 | CronJob Gotchas |
| job-rbac.yaml | apiVersion: v1 | Security |
| deployment-vs-job.yaml | apiVersion: apps/v1 | Alternatives to Jobs |
Key takeaways
concurrencyPolicy and startingDeadlineSeconds to handle overlaps and missed runs.Interview Questions on This Topic
What happens if a Job pod is evicted due to node pressure?
Frequently Asked Questions
20+ years shipping production infrastructure and CI/CD at scale. Drawn from code that ran under real load.
That's Kubernetes. Mark it forged?
5 min read · try the examples if you haven't