Skip to main content
Tools Harbor

Kubernetes CronJob Schedule Generator

Build Kubernetes CronJob schedule strings, preview the next runs, and copy a complete CronJob YAML manifest.

* * * * *

Every minute of every day

Next 5 runs

Calculating…

Common schedules

Syntax

  • * — any value
  • 5 — exact value
  • 1,3,5 — list of values
  • 1-5 — inclusive range
  • */15 — every 15 units (also 0/15 in AWS / Quartz)

A Kubernetes CronJob schedule is a standard 5-field Unix cron string set in spec.schedule0 9 * * 1-5 runs every weekday at 9 AM. The form above generates the schedule string and a complete CronJob YAML manifest with timezone, concurrency policy and starting-deadline fields filled in correctly.

What is the cron syntax used by Kubernetes CronJob?

minute  hour  day-of-month  month  day-of-week
  0      9         *          *        1-5
 0–59  0–23      1–31       1–12     0–6 (Sun=0)

Above: 0 9 * * 1-5 — every weekday at 9 AM. Same exact syntax you’d put in a Linux crontab.

Kubernetes also accepts shortcuts: @yearly, @monthly, @weekly, @daily, @hourly. Most tooling expands them in pre-flight, so the equivalents in the table below produce identical behavior. Use whichever is more readable for you.

Common Kubernetes CronJob schedules

ScheduleExpression
Every minute* * * * *
Every 5 minutes*/5 * * * *
Every 15 minutes*/15 * * * *
Every 30 minutes*/30 * * * *
Every 2 hours0 */2 * * *
Every hour0 * * * *
Twice a day (8 AM and 8 PM)0 8,20 * * *
Daily at midnight0 0 * * * (@daily)
Daily at 2 AM0 2 * * *
Every weekday at 9 AM0 9 * * 1-5
Every Monday at 9 AM0 9 * * 1
Every Sunday at 2 AM0 2 * * 0
First of every month0 0 1 * * (@monthly)
Every Sunday at midnight0 0 * * 0 (@weekly)
Once a year (Jan 1)0 0 1 1 * (@yearly)

What does a complete CronJob YAML manifest look like?

Once you’ve picked a schedule, here’s a full CronJob spec you can paste in:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: nightly-cleanup
  namespace: default
spec:
  schedule: "0 2 * * *"
  timeZone: "America/Los_Angeles"
  concurrencyPolicy: Forbid
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 1
  startingDeadlineSeconds: 300
  jobTemplate:
    spec:
      backoffLimit: 2
      activeDeadlineSeconds: 1800
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: cleanup
              image: my-org/cleanup:1.4.0
              command: ["/usr/local/bin/cleanup", "--days=7"]
              resources:
                requests:
                  cpu: 100m
                  memory: 128Mi
                limits:
                  memory: 256Mi

The bits that matter:

  • schedule — the cron string you generated above.
  • timeZone — IANA timezone (added in Kubernetes 1.25, stable in 1.27). Without it, the schedule runs in the controller’s timezone, usually UTC.
  • concurrencyPolicy: Forbid — refuses to start a new run while the previous is still going. Defaults to Allow.
  • startingDeadlineSeconds: 300 — if the controller can’t start the run within 5 minutes of its scheduled time, skip it. Without this, missed runs catch up indefinitely after a controller outage, which can cause a thundering herd.
  • successfulJobsHistoryLimit / failedJobsHistoryLimit — how many completed Job objects to keep around. Defaults are 3 and 1 — explicit is good practice.
  • backoffLimit — how many times the underlying Job retries on failure before giving up.
  • activeDeadlineSeconds — kills any single run that goes past 30 minutes here. Pair with restartPolicy: OnFailure.

Three CronJob mistakes that bite in production

1. Default concurrency. concurrencyPolicy defaults to Allow. If your job sometimes runs longer than its schedule interval (a 5-minute schedule with an 8-minute run), you’ll quietly stack up parallel jobs that fight over the same resources. Always set this explicitly.

2. No timezone before 1.25. Pre-1.25 clusters run schedules in the controller’s local time, which is whatever the cloud provider set — usually UTC, but not guaranteed. If you wrote 0 9 * * * thinking “9 AM Pacific” and your controller is on UTC, you got 9 AM UTC = 1 or 2 AM PT. Upgrade to 1.27+ and use timeZone.

3. startingDeadlineSeconds interaction with controller restarts. If you set this too short (under 10s) and the controller restarts during a deployment, you’ll see MissedSchedule events and silently dropped runs. 60–300 seconds is the safer range for most jobs. Don’t set it at all if missed runs must eventually fire.

Suspending a CronJob without losing the next run

You can pause a CronJob in place with spec.suspend: true:

kubectl patch cronjob nightly-cleanup -p '{"spec":{"suspend":true}}'

While suspended, the controller stops creating new Job objects. The catch — every scheduled time during the pause is recorded as a missed run. If more than 100 misses pile up before you unsuspend, the controller refuses to start any further runs and logs:

Cannot determine if job needs to be started.
Too many missed start time (> 100).
Set or decrease .spec.startingDeadlineSeconds or check clock skew.

A schedule of */5 * * * * hits the 100-miss limit in about 8 hours, so an overnight suspension is enough to brick the CronJob.

The fix is to set startingDeadlineSeconds to a small window — anywhere from 60 to 300 seconds is typical. The controller then only counts misses inside that window instead of from the last successful run. The next scheduled time after you unsuspend runs normally; everything before the window is silently ignored, which is what you want for most batch jobs.

spec:
  suspend: false
  startingDeadlineSeconds: 180
  schedule: "*/5 * * * *"

If you genuinely need every missed run to fire (e.g. billing rollups), don’t use suspend at all — disable the workload at the application layer and keep the CronJob ticking, or recompute the missed runs from a checkpoint when the system comes back.

When should I use KEDA’s cron trigger instead of a CronJob?

A CronJob runs a Job on a schedule — a fresh pod that does its work and exits. KEDA’s cron scaler does something fundamentally different: it scales an existing Deployment up at a start time and back down at an end time, so a long-running service handles the load while it’s “on” and scales to zero when it’s “off”.

Use casePick
Run a script periodically and exit (backups, cleanup, reports)CronJob
Scale a service up during business hours, down at nightKEDA cron scaler
Sub-minute schedules (every 30s, every 10s)KEDA cron or external scheduler
Single one-shot run at a specific timeJob (no cron)
Predictable load windows on an existing DeploymentKEDA cron scaler
Coexist with HPA on the same DeploymentKEDA cron (CronJob doesn’t manage replicas)

A KEDA ScaledObject with a cron trigger looks like this:

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: business-hours
spec:
  scaleTargetRef:
    name: my-api
  minReplicaCount: 0
  maxReplicaCount: 10
  triggers:
    - type: cron
      metadata:
        timezone: America/Los_Angeles
        start: 0 8 * * 1-5
        end: 0 18 * * 1-5
        desiredReplicas: "5"

The two patterns can coexist in the same cluster. Use a CronJob for batch work, KEDA cron for traffic shaping.

Frequently asked questions

Which cron syntax does Kubernetes CronJob use?
Standard 5-field Unix cron — minute, hour, day-of-month, month, day-of-week. The kube-controller-manager uses a Go cron parser that also accepts shortcuts like `@daily`, `@hourly`, `@weekly` and `@monthly`. Day-of-week is 0–6 with Sunday=0 (with `7` also acceptable for Sunday). No seconds field, no year field, no `?` wildcard — those are AWS/Quartz extensions.
What timezone does my CronJob run in?
Before Kubernetes 1.25, all CronJobs ran in the timezone of the kube-controller-manager (usually UTC). Since 1.25 (stable in 1.27), CronJobs accept a `spec.timeZone` field that takes any IANA timezone name like `America/Los_Angeles`. If `timeZone` is unset, the schedule is interpreted in the controller's local timezone — verify before relying on local times. Note: the older `CRON_TZ=` / `TZ=` prefix inside the `schedule` string was never officially supported, and as of Kubernetes 1.29 it produces a validation error on create or update — use `spec.timeZone` instead.
How do I prevent two copies of my job from running at the same time?
Set `spec.concurrencyPolicy: Forbid`. The default is `Allow`, which lets multiple instances run if a previous run hasn't finished. The other option, `Replace`, kills the running job and starts a new one. `Forbid` is usually what you want for batch jobs that mutate state.
Why did my CronJob skip a scheduled run?
CronJobs have a `spec.startingDeadlineSeconds` — if more than that many seconds elapse past the scheduled time without the job starting, Kubernetes skips the run and records `MissedSchedule`. Default is no deadline, but if you've set one and the controller was unavailable longer than the deadline, runs are silently dropped. Common cause: control plane downtime during an upgrade.
Can I run a CronJob more frequently than once per minute?
No — Kubernetes CronJob does not support sub-minute schedules. The cron syntax has no seconds field. For sub-minute or event-driven dispatch, use a long-running Deployment with internal scheduling, KEDA with a cron-trigger scaler (which does support seconds via a different mechanism), or an external workflow engine like Argo Workflows or Temporal.
Why won''t my CronJob run again after I unsuspended it?
When `spec.suspend: true`, every scheduled time during the pause counts as a missed run. If more than 100 missed schedules accumulate, the CronJob controller refuses to start any further runs and logs `Cannot determine if job needs to be started. Too many missed start time (> 100). Set or decrease .spec.startingDeadlineSeconds or check clock skew.` Set `startingDeadlineSeconds` to a small window (60–300s) so the controller only counts misses inside that window — the next scheduled time then runs normally instead of being blocked by the 100-miss safety limit.
When should I use KEDA''s cron trigger instead of a CronJob?
A `CronJob` runs a Job (a fresh pod that exits) on a schedule. KEDA's cron scaler does something different — it scales an existing Deployment up at a start time and back down at an end time, so a long-running service handles the load while it's "on" and scales to zero when it's "off". Use a CronJob for batch work that should start, do its thing and exit. Use KEDA cron for predictable load windows on an existing service (business-hours scaling, scheduled warm-ups, off-hours scale-to-zero). They solve different problems and can coexist in the same cluster.