Skip to main content
governance · kubernetes

CronJobs that have never fired once

resource types
1
rule IDs covered
3
severity
medium

What does ZopNight detect here?

A CronJob with no lastScheduleTime has never run since it was created. Unlike a suspended job this one is active and simply never triggered, usually a cron expression that is valid syntax but describes a moment that has not arrived, such as day 31 in a 30-day month.

Signal and threshold

How ZopNight evaluates CronJobs that have never fired once.
Field Value
Rule IDsRC-1734 · RC-1834 · RC-1934
Categorygovernance
Severitymedium
Metricnone — pure configuration read
Sourceinternal/rules/k8s/cronjob_never_scheduled.go

Active, enabled, and still never fired

This is not the suspended case. The CronJob is enabled, the controller is watching it, and status.lastScheduleTime is empty. Kubernetes considers everything healthy.

The schedule is syntactically valid (an invalid one would be rejected at admission), but it describes a time that has not occurred since the object was created.

The cron expressions that do this

Impossible or near-impossible dates. 0 0 31 2 * is the 31st of February, which never arrives. 0 0 30 * * skips February entirely and fires eleven times a year.

Timezone assumptions. Kubernetes CronJob schedules evaluate in UTC unless the cluster runs a version supporting spec.timeZone and it is set. A job written for 2am local time may be firing at a very different hour, or a maintenance window may have been missed entirely.

Day-of-month and day-of-week together. In cron these are ORed, not ANDed. 0 0 1 * 1 means “the 1st of the month or any Monday”, which is almost never what the author intended, though it fires often rather than never.

A far-future first occurrence. An annual job created in March with a January schedule legitimately shows nothing for ten months, and is not a defect.

Distinguishing broken from simply not due yet

Compare the creation timestamp against the schedule’s natural period. A daily job created a week ago with no run is broken. An annual job created last month is fine.

That check is why the rule surfaces this for review rather than asserting a fault: it cannot know your intended cadence.

Finding CronJobs with a null lastScheduleTime

Terminal window
kubectl get cronjob -A -o json | jq -r '
.items[] | select(.status.lastScheduleTime == null and (.spec.suspend != true))
| "\(.metadata.namespace)/\(.metadata.name)\t\(.spec.schedule)\tcreated=\(.metadata.creationTimestamp)"'

Read the schedule column against the creation date. That pairing answers it faster than anything else.

See it fire on your bill.

Connect an account read-only. The first findings land in minutes.

417 rule families across 353 resource types on 22 platforms. Every threshold, metric, and IAM action is documented on these pages before you grant anything.

417 rule families documented
353 resource types covered
read-only default access level
Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console· Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console·