Cron for backend engineers: fields, pitfalls, and time zones
Cron looks small until a job runs twice on Sunday, skips a month-end, or fires eight hours off because the container image inherited UTC while the runbook assumedAsia/Shanghai.
The five fields
Traditional cron: minute hour day-of-month month day-of-week. Some platforms add seconds or year fields. Always confirm which dialect your scheduler uses (Vixie, Quartz, Kubernetes CronJob, cloud scheduler UI…).
Day-of-month and day-of-week
When both are restricted (not *), behavior differs by implementation: some treat the combination as OR, others as AND. If your expression sets both, read the docs for that exact scheduler—or rewrite the schedule to avoid the ambiguity.
Timezone is part of the API
A cron string without a documented timezone is incomplete. Prefer an explicit TZ in the platform config. For worldwide users, ask whether the job should follow UTC (good for infra) or a business region (good for “9am office” reports).
Operational habits
- Name jobs after outcomes (
rotate-staging-tokens), not times. - Emit a structured “job started/finished” log with the schedule id.
- Alert on miss as well as failure—silent skips hurt more than loud errors.
- Load-test the “everyone’s cron hits midnight UTC” thundering herd.
Paste a schedule into the Cron Expression Explainer for a quick sanity check, then verify on the real scheduler.