Log In

A Checklist for Reliable Scheduled Jobs

Scheduled jobs power backups, reports, and maintenance tasks, yet they break when the next run arrives while the prior one still executes or when the clock changes. Reliable automation requires explicit rules for timing, overlap handling, time zones, missed executions, and observability. POSIX cron and modern schedulers provide the primitives; the checklist below turns those primitives into dependable behavior.

Use IANA Time Zones, Never Offsets

The timezone field must contain an IANA database name such as America/New_York or Europe/London. These names encode daylight-saving transitions and historical changes. Fixed offsets and abbreviations such as +05:00 or EST produce incorrect firings twice a year when rules shift.

Kubernetes CronJobs accept a top-level timeZone field. GitHub Actions accepts an IANA string in the schedule.timezone property. Both evaluate the cron expression against the named zone rather than the controller's local clock. POSIX crontab implementations rely on the TZ environment variable or system zone, but the same IANA names remain the only portable choice.

Production Rule Every scheduled job must declare an overlap policy, an IANA time zone, and fields that record last_run_at, next_run_at, and run_count. Without these, operators cannot detect missed triggers or verify schedules.

Define an Overlap Policy

An overlap occurs when a new scheduled time arrives while the previous run remains active. The overlap_policy field decides the outcome. The safest default for idempotent work is skip: the new occurrence is dropped, next_run_at advances, and a cron.skipped event is emitted.

Kubernetes offers Allow (default), Forbid, and Replace. Forbid behaves like skip. Replace terminates the running job and starts a fresh one. GitHub Actions relies on concurrency groups and cancel-in-progress to achieve similar control. Choose the policy that matches the job's side effects rather than relying on the platform default.

Track Execution History

System-managed fields give operators visibility without waiting for the next trigger. next_run_at shows the upcoming execution. last_run_at and run_count reveal whether triggers were missed. created_at supports auditing of schedule changes.

When the scheduler wakes after a pause or clock skew, it must fire at most one catch-up occurrence per job and advance next_run_at to the next future time. This prevents a flood of missed runs from overwhelming the system.

Handle Missed Runs and Deadlines

Kubernetes CronJobs use startingDeadlineSeconds to decide how late a job may start before it is counted as missed. Values under ten seconds can prevent scheduling entirely. When the limit is exceeded, the controller skips the instance and continues with future occurrences.

If more than one hundred schedules are missed, the CronJob controller stops creating jobs and logs an error. GitHub Actions evaluates schedules on the default branch and skips runs during high load; the five-minute minimum interval remains the shortest supported cadence.

Add Locks, Logs, and Monitoring

Application-level locks prevent duplicate work when the scheduler itself allows concurrency. Record every decision—start, skip, replace—in structured logs that include the scheduled time, actual start time, and outcome. These logs become the audit trail when investigating missed or duplicate runs.

Combine scheduler fields with external monitoring. A simple query that compares last_run_at against the expected interval catches silent failures faster than waiting for downstream data to look wrong.

Sources

See our free AI tools →