DEV Community

Skojio Community
Skojio Community

Posted on • Originally published at skojio.com

Three cron mistakes that quietly break overnight jobs

A cron expression is five fields. How wrong can it go? Quite wrong, as it turns out — and the failure mode is usually silent. The job runs, it just runs at the wrong time, or sixty times in a row, or never on the day you actually need it.

Here are the three patterns I've watched break production overnight, and how to spot them before they bite.

Mistake 1 — Treating */5 as "every 5 from now"

*/5 * * * * does not mean "run every 5 minutes starting from when you save this". It means "run at minutes 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55 of every hour".

If you save the cron at 14:03 expecting the first run at 14:08, you will be wrong. The first run will be at 14:05.

Worse, */7 * * * * runs at minutes 0, 7, 14, 21, 28, 35, 42, 49, 56 — and then 0 again 4 minutes later, because the next hour resets the cycle. Anything that isn't a divisor of 60 will misbehave at the hour boundary.

Mistake 2 — Day-of-month AND day-of-week

The fifth field (day of week) and the third field (day of month) have a non-obvious interaction. If either is unrestricted (*), cron treats the other as the active filter. If both are specified, cron runs when either matches — an OR, not an AND.

So 0 9 1 * 1 does NOT mean "9am on Mondays that fall on the 1st". It means "9am on the 1st of every month, AND 9am every Monday". That's roughly 8 runs per month instead of zero or one.

If you want a true AND, you have to filter inside the script:

[ "$(date +\%u)" -eq 1 ] && /path/to/job.sh
Enter fullscreen mode Exit fullscreen mode

Or use a cron-like scheduler that supports compound conditions natively (Quartz, for instance).

Mistake 3 — Timezone drift

Most cron daemons run in the system's local timezone, which is often UTC on cloud VMs and often local time on developer machines.

Two failure modes:

  1. DST transitions. A cron set to 0 2 * * * in a region that observes daylight saving will either skip a day (spring forward) or run twice (fall back) on transition nights.
  2. Container drift. A container's TZ defaults to UTC. If your developer laptop says BST and the production container says UTC, the same crontab runs at different wall-clock times.

The fix is to always be explicit:

# In crontab itself (Linux):
CRON_TZ=Europe/London
0 2 * * * /path/to/backup.sh
Enter fullscreen mode Exit fullscreen mode

Or run everything in UTC and convert in the application layer.

A safer authoring loop

Before you commit a cron expression to production, paste it into a parser that shows you the next 10 fire times. If the times match what you expected, ship it. If they don't, you've caught the mistake before it costs you a night.

The Skojio cron helper does exactly that — paste an expression, see the next 10 fire times in your chosen timezone, plus a plain-English description of what the expression actually means. It catches all three of the mistakes above in seconds.

Recap

Mistake How to spot it
*/N where N doesn't divide 60 Hour-boundary glitch
Day-of-month + day-of-week both set More runs than expected
Implicit timezone DST or container drift

Cron is fine when you respect it. The above three failure modes account for nearly every "why did this run / not run" Slack thread I've seen.

Top comments (0)