How Cron Schedules Really Work (and the Mistakes That Bite)
By the UtilityForge Editorial Team · Published October 22, 2026 · 6 min read
Almost everyone who runs a server ends up staring at a line like */15 9-17 * * 1-5 and quietly hoping it does what they think it does. Cron is old, terse and unforgiving. It's also everywhere: backups, report emails, cache clearing, certificate renewals. So it's worth ten minutes to understand it properly.
The five fields, left to right
A standard cron line has five time fields followed by the command. In order they are minute (0-59), hour (0-23), day of month (1-31), month (1-12) and day of week (0-7, where both 0 and 7 mean Sunday). An asterisk means "every value". So 0 3 * * * reads as: minute 0, hour 3, every day. That's 3:00 a.m. daily.
A handy trick is to read the line out loud, field by field, the way you'd read a sentence. "At minute zero, at hour three, on every day of the month, in every month, on every day of the week." If it sounds right, it probably is.
Lists, ranges and steps
Three small pieces of syntax cover most real-world needs:
- Lists use commas.
0 8,12,18 * * *runs at 8:00, 12:00 and 18:00. - Ranges use a dash.
0 9-17 * * *runs on the hour from 9 a.m. to 5 p.m. - Steps use a slash.
*/10 * * * *runs every ten minutes. You can combine them too:0-30/10means minutes 0, 10, 20 and 30.
Most modern cron implementations also accept names, so MON-FRI works in the weekday field and JAN in the month field. A few offer shortcuts like @daily and @weekly, which are easier to read when you come back to the file six months later.
The day-of-month and day-of-week trap
This one catches experienced people. If you restrict both the day-of-month and the day-of-week fields, classic cron doesn't require both to match. It runs the job when either one matches. So 0 0 13 * 5 doesn't mean "Friday the 13th". It means "every 13th, and also every Friday". If you really want Friday the 13th, the usual workaround is to schedule it for every Friday and have the script itself check whether the date is the 13th.
Time zones and the server clock
Cron uses the time zone of the machine (or sometimes of the cron daemon's environment). A cloud server is often set to UTC, while you're thinking in local time. If you're in India, a job set for 0 9 * * * on a UTC box will fire at 2:30 in the afternoon your time. Always check with date on the server before you trust a schedule.
Daylight saving makes it messier in countries that use it. A job set for 2:30 a.m. may run twice, or not at all, on the night the clocks change. For anything critical, pick a time that never falls in that gap, or run the server in UTC and avoid the problem altogether.
Five mistakes worth avoiding
- Using
* * * * *by accident. Leave a field blank or mistype a step and you can end up running a heavy script every single minute. - Forgetting that cron has a tiny environment. Your shell's PATH isn't loaded. Use full paths to commands, e.g.
/usr/bin/python3. - Ignoring output. By default cron emails output to the local user, which nobody reads. Redirect to a log file:
>> /var/log/myjob.log 2>&1. - Overlapping runs. If a job takes eleven minutes and you run it every ten, you'll have two copies fighting. Use a lock, for example with
flock. - Not testing the schedule itself. This is the cheapest fix of all. Before you save a new line, check when it will actually fire.
How to sanity-check a schedule
Write the expression, then look at the next five or ten run times. If you meant "weekdays at 9" and the list includes a Saturday, you'll spot it immediately. Our cron expression explainer does this in your browser and breaks each field down in plain words, which is handy when you inherit somebody else's crontab and can't remember what 5-59/6 means.
A few useful patterns to keep handy
0 2 * * *– nightly at 2 a.m., a classic backup slot*/5 * * * *– every five minutes0 9 * * 1-5– weekdays at 930 2 1 * *– the first of each month at 2:300 0 * * 0– Sunday at midnight
Cron isn't going anywhere, and the syntax rewards a little care. Learn the five fields, remember the day-of-month quirk, log your output, and test before you ship. That's most of the battle.