Cron Scheduling Explained: The Five Fields and the Day-of-Month/Week Trap
Cron runs a command on a schedule defined by five time fields: minute, hour, day of month, month, and day of week. The syntax is compact and easy to get almost right, and one specific combination of fields behaves differently than most people expect.
Last updated: August 30, 2026
The five fields
A standard crontab line has five time fields followed by the command to run: minute (0-59), hour (0-23), day of month (1-31), month (1-12, or JAN-DEC), and day of week (0-7, where both 0 and 7 mean Sunday, or SUN-SAT). A job runs whenever the current time matches every one of those fields, with one exception covered below. An asterisk in any field means "every value," so * * * * * runs every single minute.
Ranges, lists, and steps
Beyond a plain number or asterisk, each field accepts a few other forms. A hyphen gives an inclusive range, so 1-5 in the day-of-week field means Monday through Friday. A comma gives a list of specific values or ranges, like 0,30 for the top and bottom of every hour. A slash after a range or an asterisk gives a step value, so */15 in the minute field means every 15 minutes starting from 0, equivalent to writing 0,15,30,45 out by hand.
The day-of-month/day-of-week trap
This is the rule that catches people out. When both the day-of-month and day-of-week fields are restricted, meaning neither one is left as an asterisk, crontab's manual page specifies that the two combine with OR, not AND. A job scheduled for 0 9 1 * 1, meant to sound like "9am on the 1st, if it's a Monday," actually runs at 9am on the 1st of every month AND every Monday, because either field matching is enough to trigger it. To restrict to a single specific weekday-of-month combination, day-of-week needs to stay an asterisk and the logic needs to move into the script itself.
The @ shorthand strings
Instead of five fields, a line can start with one of a small set of special strings: @yearly or @annually (equivalent to 0 0 1 1 *), @monthly (0 0 1 * *), @weekly (0 0 * * 0), @daily (0 0 * * *), and @hourly (0 * * * *). @reboot is different from the rest: it isn't a time-based schedule at all, it just runs the command once every time the system boots.
Common mistakes
- Restricting both day-of-month and day-of-week and expecting AND logic instead of the actual OR behavior.
- Forgetting that cron typically runs in the system's local timezone, or a timezone configured for the cron daemon, not necessarily the one the schedule was designed around.
- Writing a step value against a range that doesn't start where expected, like
5-30/10, which starts at 5, not 0. - Assuming a missing crontab entry means the job never ran, rather than checking the mail or logs cron actually delivered its output to.
Paste an existing schedule to see it in plain English, or build one from a simple form, entirely in your browser.
Frequently asked questions
What do the five fields in a cron expression mean?
In order: minute (0-59), hour (0-23), day of month (1-31), month (1-12 or JAN-DEC), and day of week (0-7, where both 0 and 7 mean Sunday, or SUN-SAT). A job runs when the current time matches all five fields, with one important exception covering day-of-month and day-of-week together.
Why did my job run on an unexpected day?
The most common cause is setting both day-of-month and day-of-week to specific values in the same expression. The crontab(5) manual page specifies that when both fields are restricted, they combine with OR: the job runs when either matches, not only when both do at once.
What's the difference between */15 and 0,15,30,45?
For the minute field, both produce the same result: a job that runs at minutes 0, 15, 30, and 45 of every hour. */15 is shorthand for a step value applied to the field's full range, while the comma-separated list spells out the same four values explicitly.
What does @reboot do?
It runs the job once, every time the system starts up, instead of on a recurring time-based schedule. It's one of several special "@" strings crontab accepts in place of the five numeric fields, alongside shorthands like @daily and @hourly.