Crontab syntax explained: the five cron fields, special characters, common schedules, the day-of-month or day-of-week rule, @daily shortcuts and time zones.
By Udit Agarwal · Updated October 18, 2026
A cron expression is a schedule written as five fields separated by spaces: minute, hour, day of the month, month and day of the week. The expression 0 9 * * 1-5, for example, means 9:00 every Monday to Friday. The format comes from cron, the Unix job scheduler, and the same crontab syntax now drives CI pipelines, Kubernetes jobs and cloud schedulers, with small differences that are worth knowing.
Each line in a crontab has five time fields followed by the command to run:
30 2 * * * /usr/local/bin/backup.sh
That line runs the backup script at 2:30 every night. The fields always come in this order:
| Position | Field | Allowed values |
|---|---|---|
| 1 | Minute | 0–59 |
| 2 | Hour | 0–23 |
| 3 | Day of month | 1–31 |
| 4 | Month | 1–12 |
| 5 | Day of week | 0–7 (0 and 7 are both Sunday) |
Hours use the 24-hour clock. Most implementations also accept three-letter names such as JAN or MON in the last two fields. System-wide files such as /etc/crontab also name a user before the command.
Four characters cover almost every schedule:
| Character | Meaning | Example | Matches |
|---|---|---|---|
* |
every value | * in the hour field |
every hour |
, |
a list | 1,15 in day of month |
the 1st and the 15th |
- |
an inclusive range | 1-5 in day of week |
Monday to Friday |
/ |
a step | */15 in the minute field |
0, 15, 30 and 45 |
A step counts from the start of its range, so 0-30/10 in the minute field means 0, 10, 20 and 30. Steps that do not divide evenly restart at the boundary: */7 in the minute field runs at 0, 7, 14 and so on up to 56, then at 0 again only four minutes later. Likewise */2 in the day-of-month field gives the odd days, so after a 31-day month the job runs on the 31st and the 1st back to back. Put the step after * or a range; a bare number before the slash, as in 5/15, means different things to different schedulers.
| Expression | When it runs |
|---|---|
*/5 * * * * |
every 5 minutes |
0 * * * * |
at the start of every hour |
0 */6 * * * |
every 6 hours: 0:00, 6:00, 12:00 and 18:00 |
0 9 * * 1-5 |
9:00 on weekdays |
*/15 9-17 * * 1-5 |
every 15 minutes from 9:00 to 17:45 on weekdays |
0 0 * * 0 |
midnight every Sunday |
0 0 1 * * |
midnight on the first day of every month |
In the business-hours line, the range 9-17 includes hour 17, so the last run is at 17:45, not 17:00. Use 9-16 if the work must stop before 17:00.
When both day fields are restricted, meaning neither is *, the job runs on days that match either field, not both. The crontab manual states the rule, and it catches anyone writing "the first Monday of the month": 0 9 1-7 * 1 runs on the 1st to the 7th of every month and on every Monday. If only one of the two fields is restricted, only that one applies.
To get the first Monday, restrict one field in cron and test the other in the command:
0 9 1-7 * * [ "$(date +\%u)" -eq 1 ] && /usr/local/bin/report.sh
Cron starts this line on days 1 to 7, and the test lets it continue only when date +%u reports 1, meaning Monday. The backslash matters because cron turns an unescaped % into a new line. Implementations disagree on a few edge cases, such as a stepped day field like */2 combined with a weekday, so test those on the scheduler you actually use.
Vixie cron, cronie and many other schedulers accept named shortcuts in place of the five fields:
| Shortcut | Same as | Meaning |
|---|---|---|
@yearly or @annually |
0 0 1 1 * |
midnight on 1 January |
@monthly |
0 0 1 * * |
midnight on the 1st |
@weekly |
0 0 * * 0 |
midnight on Sunday |
@daily or @midnight |
0 0 * * * |
midnight every day |
@hourly |
0 * * * * |
the start of every hour |
@reboot |
no equivalent | once, when cron starts |
They read well but are less portable, and they all fire at minute 0. On a busy server every @daily job then starts in the same second, so spreading jobs across odd minutes, such as 17 3 * * *, avoids the pile-up.
Several schedulers add a field, which makes copied expressions a classic source of bugs:
| Scheduler | Fields | Notes |
|---|---|---|
| Unix cron, Kubernetes, GitHub Actions | 5, minute first | the format above |
| Quartz (Java) | 6 or 7, seconds first, optional year last | day of week 1–7 with 1 = Sunday; ? in one day field |
Spring @Scheduled |
6, seconds first | day of week 0–7, with 0 and 7 = Sunday |
| AWS EventBridge | 6, minute first, year last | ? in one day field |
So "noon every day" is 0 12 * * * in a crontab, 0 0 12 * * ? in Quartz and cron(0 12 * * ? *) in EventBridge. Quartz also adds L for the last day, W for the nearest weekday and # for the nth weekday, as in 6#3 for the third Friday. Watch the weekday numbers when you translate: 1 is Monday in a crontab but Sunday in Quartz.
Classic cron runs jobs in the time zone of the machine it runs on, and many servers and containers are set to UTC. A job written as 0 9 * * 1-5 on a UTC server runs at 9:00 UTC: 14:30 in Bengaluru, 10:00 in London during British Summer Time and 5:00 in New York during daylight saving time. Hosted schedulers such as GitHub Actions use UTC by default. Others let you choose: Kubernetes CronJobs have a timeZone field, and cronie reads a CRON_TZ variable.
If the scheduler follows a local zone with daylight saving, two nights a year are irregular. When clocks spring forward, an hour never happens, so a job at 30 2 * * * has no 2:30 that night in New York, Paris or Berlin. When clocks fall back, an hour happens twice. Vixie cron and its descendants run jobs from a skipped hour soon after the jump and do not repeat fixed-time jobs in a repeated hour, but other schedulers behave differently. The safe choice is to run schedulers in UTC, or to keep important jobs away from the clock-change hours, usually 1:00 to 4:00 local time. The World Clock & Time Zone Converter shows what a UTC schedule means in your city, and the time zone guide explains how offsets shift.
When a job works in your terminal but not from cron, check these:
PATH is short, often just /usr/bin:/bin. Use absolute paths.% into a new line, which breaks commands such as date +%F. Write \%.>> /var/log/job.log 2>&1 to keep it.flock on Linux.The Cron Expression Explainer describes a five-field expression in a sentence. For 0 9 * * 1-5 it prints "Runs at minute 0, past hour 9, on every day-of-month, in every month, on Mon, Tue, Wed, Thu, Fri." and lists the next six run times in your browser's local time zone. It looks up to a year ahead, so an impossible date such as 0 0 30 2 * (30 February) shows no upcoming runs. It accepts numbers with *, ,, - and / in the standard ranges, including 7 for Sunday, and applies the either-day rule. Month and weekday names, @ shortcuts, six-field expressions and Quartz characters such as ? and L show an error, so translate them into the plain five-field form first.
To build an expression instead, the Cron Expression Generator sets each field to Every, Every N, Specific values or a Range, with names such as Jan and Mon on the buttons and numbers in the output. It shows the expression with a copy button, a plain-English summary and the next five runs, and has presets for common schedules. Both tools run in your browser, and nothing you type is uploaded.
What is a cron expression? A cron expression is a schedule written as five fields: minute, hour, day of month, month and day of week. The scheduler checks it every minute and runs the job when the current time matches. Some schedulers add a sixth field for seconds or the year.
What time zone does cron use? Classic cron uses the time zone of the server it runs on, which is often UTC. Hosted schedulers such as GitHub Actions use UTC by default, while Kubernetes and cronie let you set a zone.
Why did my cron job run on the wrong day? Most likely both the day-of-month and day-of-week fields are set. When neither is an asterisk, cron runs the job on days that match either field, so a schedule meant for the first Monday runs on the 1st to the 7th and on every Monday.
Does cron support seconds? Standard crontab does not; its smallest unit is one minute. Quartz and Spring add a seconds field at the start, making six fields. In plain cron, a task that must run more than once a minute needs a loop in the script or a different tool, such as a systemd timer.
What does @daily mean in crontab? It is a shortcut for midnight every day: minute 0 and hour 0, with every other field left open. The other shortcuts are @hourly, @weekly, @monthly, @yearly and @reboot, which runs once when cron starts.
How do I run a cron job on the last day of the month? Standard cron has no symbol for it. The usual workaround is to schedule the job on days 28 to 31 and have the command check that tomorrow is the 1st. Quartz-style schedulers accept L in the day-of-month field instead.
All Date / Time tools · All guides