How to Schedule a Recurring Task With a Kubernetes CronJob

A recurring cluster task - pruning old rows, nightly backups, report rollups - belongs in a Kubernetes CronJob, not a hand-run script. Set a schedule in standard cron syntax (for example */5 * * * *), give it a jobTemplate that runs a container and exits, and add successfulJobsHistoryLimit: 3 so completed Jobs do not accumulate. Here is the full manifest and how to apply it.

Kubernetes Engineerkuberneteskubectlcronjob

What a CronJob actually does

A Kubernetes CronJob is the in-cluster equivalent of a crontab entry. At each time its schedule fires, the CronJob controller creates a Job, and that Job runs a pod that executes your task and exits. The three parts you always set are the schedule (when it runs), the jobTemplate (what it runs), and the history limits (how many finished Jobs to keep around).

The schedule field uses standard five-field cron syntax:

┌───────── minute (0-59)
│ ┌─────── hour (0-23)
│ │ ┌───── day of month (1-31)
│ │ │ ┌─── month (1-12)
│ │ │ │ ┌─ day of week (0-6, Sun=0)
│ │ │ │ │
*/5 * * * *   → every 5 minutes
0 * * * *     → top of every hour
0 3 * * *     → 03:00 every day (a nightly backup)

Write the CronJob manifest

Create cronjob.yaml. This one runs a cleanup task every five minutes and keeps only the last three successful Jobs:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: cleanup
spec:
  schedule: "*/5 * * * *"
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 1
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: Never
          containers:
          - name: cleanup
            image: busybox:1.36
            command: ["sh", "-c", "echo pruning old rows; sleep 1"]

A few details that matter:

Apply and verify

kubectl apply -f cronjob.yaml
kubectl get cronjob cleanup

kubectl get cronjob shows the schedule, the suspend state, and the last-run columns:

NAME      SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
cleanup   */5 * * * *   False     0        <none>          8s

LAST SCHEDULE stays <none> until the schedule first fires. You do not have to wait five minutes to test the task - trigger a one-off Job from the CronJob's own pod spec:

kubectl create job --from=cronjob/cleanup test-run
kubectl logs job/test-run

That runs the exact same container immediately, so you can confirm it does the right thing and exits cleanly before trusting the schedule.

Stop overlapping runs

If a run can take longer than the interval between runs, add a concurrencyPolicy so a slow Job does not overlap the next scheduled one:

spec:
  schedule: "*/5 * * * *"
  concurrencyPolicy: Forbid

Forbid skips the new run if the previous one is still going; Replace kills the running Job and starts fresh. The default, Allow, lets them overlap - which is usually not what you want for a cleanup or backup task. Getting the schedule right and choosing the concurrency policy are the two things people most often get wrong.

Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.

Try it in a workspace →

What you'll practice

FAQ

How do I schedule a recurring task in Kubernetes?

Create a CronJob with a schedule in standard cron syntax and a jobTemplate that runs your container. For example schedule "*/5 * * * *" runs every five minutes; apply it with kubectl apply -f cronjob.yaml and confirm with kubectl get cronjob.

What cron schedule format does a Kubernetes CronJob use?

It uses standard five-field cron syntax: minute, hour, day-of-month, month, day-of-week. So "0 3 * * *" runs at 03:00 daily and "*/5 * * * *" runs every five minutes. The controller schedules a Job each time the expression matches.

How do I stop finished Kubernetes Jobs from piling up?

Set successfulJobsHistoryLimit and failedJobsHistoryLimit on the CronJob spec. They cap how many completed and failed Jobs (and their pods) are retained, so old runs are garbage-collected instead of cluttering the namespace and consuming its quota.

How do I manually trigger a Kubernetes CronJob to test it?

Run kubectl create job --from=cronjob/<name> <job-name>. This creates a one-off Job using the CronJob's exact pod spec, so you can run the task immediately and read its logs with kubectl logs job/<name> without waiting for the schedule to fire.

Keep learning

Add Env Vars to a Kubernetes CronJobKubernetes projectUnstick a Kubernetes Job That Never FinishesKubernetes projectMount a ConfigMap in a Kubernetes PodKubernetes projectKubernetes roadmapStep by step to hiredKubernetes interview questionsSTAR answersAll Kubernetes projectsProjects hub

Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.

Open the workspace →