Crontab syntax

Five fields, smallest unit first, then the command. Below, each field is explained and the common expressions are shown with the actual times they produce — computed, not asserted.

The five fields

┌─ minute         0-59
│ ┌─ hour         0-23
│ │ ┌─ day of month   1-31
│ │ │ ┌─ month     1-12  or JAN-DEC
│ │ │ │ ┌─ day of week   0-7  or SUN-SAT   (0 and 7 both = Sunday)
│ │ │ │ │
30 3 * * 1-5   /usr/local/bin/backup.sh

Everything after the fifth field is the command. The ordering catches people because it climbs from minutes to months and then drops back to a day of the week, so the example reads as half past three on weekdays.

If your expression has six or seven fields, it is not Unix cron. Quartz — used by Java schedulers, Spring's @Scheduled and several cloud services — prepends seconds and can append a year. Pasting one into a crontab shifts every field one position and produces a schedule at an entirely different time rather than an error.

What each field accepts

FormMeaningExample
*every value in the field* * * * * — every minute
5one value5 * * * * — five past every hour
1-5a range, inclusive* * * * 1-5 — weekdays
1,15a list0 0 1,15 * * — 1st and 15th
*/15step across the range*/15 * * * * — :00 :15 :30 :45
MONname instead of number0 9 * * MON-FRI

Steps deserve a note because the rule is not what most people assume: the step divides the range in front of the slash, not the clock. 10-40/15 gives 10, 25 and 40. And */7 in the minute field is uneven across the hour — 0, 7, 14 … 56, then back to 0 four minutes later — because the range restarts rather than the cycle continuing.

Common expressions, with their real next runs

Computed with the parser on this site, starting from 1 September 2026, 00:00 UTC:

*/5 * * * *     -> 09-01 00:05 | 09-01 00:10     every 5 minutes
0 * * * *       -> 09-01 01:00 | 09-01 02:00     hourly, on the hour
0 0 * * *       -> 09-02 00:00 | 09-03 00:00     daily at midnight
0 */6 * * *     -> 09-01 06:00 | 09-01 12:00     every 6 hours
30 3 * * 1-5    -> 09-01 03:30 | 09-02 03:30     weekdays at 03:30
0 0 * * 0       -> 09-06 00:00 | 09-13 00:00     Sundays
0 0 1 * *       -> 10-01 00:00 | 11-01 00:00     1st of the month
15 14 1 * *     -> 09-01 14:15 | 10-01 14:15     1st at 14:15

The asterisk is not neutral

In the two day fields, * does more than say "unspecified". Leaving day-of-month as * is precisely what makes day-of-week the sole constraint. Restrict both and cron switches to combining them with OR, firing on either match — the most common reason a schedule runs far more often than intended. The full explanation is in cron day of week.

Shorthand macros

Most implementations accept @yearly, @monthly, @weekly, @daily and @hourly in place of all five fields; @daily is exactly 0 0 * * *. @reboot is different in kind — it runs when cron starts, which means at boot but also whenever the daemon is restarted, so it can fire more often than the name implies. None of these are in the POSIX specification, so write the fields out where portability matters.

Why it works in your shell but not in cron

Cron runs with a minimal environment and a short PATH, so the usual cause is a binary that cannot be found or a variable your script assumed. Use absolute paths for the interpreter and the script, and set what you need at the top of the crontab.

The other frequent surprise is the percent sign. Cron treats an unescaped % as the end of the command, sending the remainder to the job as standard input — so a date format like +%Y-%m-%d silently truncates your command unless you write +\%Y-\%m-\%d.

Frequently asked questions

What are the five fields in a crontab line?

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 or SUN-SAT, where both 0 and 7 mean Sunday). Everything after the fifth field is the command to run. The order trips people up because it goes from the smallest unit to the largest and then jumps back to a day, so 30 3 * * 1-5 reads as "minute 30, hour 3, any day of month, any month, Monday to Friday" — half past three on weekdays. If your expression has six or seven fields you are looking at Quartz syntax, not Unix cron, and pasting it into a crontab shifts every field one position rather than raising an error.

What does the asterisk mean in cron?

It matches every value the field allows, so * in the hour field means all twenty-four hours rather than any particular one. The subtlety is that an asterisk is not neutral in the two day fields: leaving day-of-month as * is what makes day-of-week act as the sole constraint. Restrict both and cron switches to combining them with OR, firing on either match — the single most common reason a job runs more often than intended. So * carries meaning beyond "unspecified", and changing one from * to a value can change how the other behaves. It is the only place in the syntax where two fields interact, which is why it surprises people who have read the field list and assumed independence.

How do step values like */15 work?

The step applies to the range in front of the slash, not to the clock. In the minute field */15 expands the implicit range 0-59 and takes every fifteenth value, giving 0, 15, 30 and 45. Writing 10-40/15 restricts the range first and yields 10, 25 and 40. A bare number with a step, such as 5/10, runs from 5 to the end of the field. This is why */7 is uneven across the hour boundary: it produces 0, 7, 14 and so on up to 56, then restarts at 0 four minutes later rather than continuing the cycle. Steps that divide the range evenly — 5, 10, 15, 20, 30 — avoid the discontinuity entirely.

What are @daily and @reboot?

They are shorthand macros most implementations accept in place of all five fields. @yearly, @monthly, @weekly, @daily and @hourly stand for the obvious schedules — @daily is equivalent to 0 0 * * *. @reboot is different in kind: it runs once when cron itself starts, which usually means at boot but also after the daemon is restarted, so it can fire more often than the name suggests. None of these are in the POSIX specification, so they are portable across common Linux crons but not guaranteed everywhere. Where portability matters, write the five fields out. @reboot in particular behaves differently across implementations and containers, where the daemon may start far more often than the machine does.

Why does my cron job work manually but not from crontab?

Almost always the environment. Cron runs with a minimal set of variables and a short PATH, so a command that works in your shell fails because the binary is not found or a variable your script relies on is absent. Use absolute paths for both the interpreter and the script, and set anything you need explicitly at the top of the crontab. The other frequent cause is the percent sign, which cron treats specially: an unescaped % ends the command and the rest becomes standard input, so date formats such as +%Y must be written as +\%Y. Setting MAILTO at the top of the crontab, or redirecting output to a log file, is what turns a silently failing job into one you can diagnose.

References

Paste an expression into the cron parser to read it in plain English and see its next five runs. More tools at withuse.io/tools.