Cron Syntax Explained
A cron expression is five fields separated by spaces: minute, hour, day of month, month and day of week. Each field takes a number, a * for every value, a range like 1-5, a list like 1,15 or a step like */10. So 0 9 * * 1-5 means 09:00 on weekdays, and */15 * * * * means every 15 minutes.
The five fields
| Position | Field | Allowed values |
|---|---|---|
| 1 | Minute | 0–59 |
| 2 | Hour | 0–23 |
| 3 | Day of month | 1–31 |
| 4 | Month | 1–12 or JAN–DEC |
| 5 | Day of week | 0–6 or SUN–SAT (7 is also Sunday) |
Special characters
*every value.* * * * *runs every minute.,a list.0 8,20 * * *runs at 08:00 and 20:00.-a range.0 9-17 * * *runs on the hour from 09:00 to 17:00./a step.*/20 * * * *runs at minutes 0, 20 and 40.5/20runs at 5, 25 and 45.
Steps count from the start of the range, not from when you saved the job. */7 in the minute field runs at 0, 7, 14 … 56, then again at 0, so the gap across the hour is 4 minutes, not 7.
Examples
| Expression | Runs |
|---|---|
*/5 * * * * | Every 5 minutes |
0 * * * * | Every hour, on the hour |
0 0 * * * | Every day at midnight |
30 6 * * 1-5 | Weekdays at 06:30 |
0 0 * * 0 | Every Sunday at midnight |
0 0 1 * * | The 1st of every month at midnight |
0 0 1 1 * | Once a year, on 1 January |
15 14 1 */3 * | 14:15 on the 1st of every third month (Jan, Apr, Jul, Oct) |
Paste any of these into the cron explainer to see the next run times.
The day-of-month and day-of-week trap
When both day fields are set to something other than *, cron runs the job if either matches. 0 0 13 * 5 does not mean Friday the 13th; it means every 13th of the month and every Friday. To get Friday the 13th, schedule on the 13th and check the weekday inside the job.
Time zones
A crontab on a Linux server uses the server's time zone. GitHub Actions schedules, Cloudflare Cron Triggers and AWS EventBridge cron expressions run in UTC. If a job "runs an hour late" after a clock change, the schedule is almost certainly in a different time zone from the one you had in mind.
Shortcuts
Most cron implementations accept @hourly, @daily (or @midnight), @weekly, @monthly, @yearly and @reboot, which runs once at startup.
Frequently asked questions
How do I run a cron job every 30 seconds?
Standard cron cannot go below one minute. Either run a job every minute that does its work twice with a 30-second sleep, or use a scheduler that supports seconds, such as a systemd timer or a Quartz-style scheduler.
How do I run a job on the last day of the month?
Standard cron has no "last day" symbol (L is a Quartz extension). A common trick is to run on days 28–31 and check in the command whether tomorrow is the 1st: 0 23 28-31 * * [ "$(date -d tomorrow +\%d)" = "01" ] && ./job.sh
Why did my cron job not run?
The usual causes are a different time zone from the one you expected, a PATH that does not include your program (cron runs with a minimal environment), an unescaped % sign in the command (cron treats % as a newline), or both day fields set so the schedule means something else.