ToolSite

Cron Expression Cheat Sheet: Common Schedules Explained

Quick reference for cron expressions: @daily, */5, 0 0 * * 0, and every schedule you'll encounter. Step values, ranges, lists, and shortcuts for crontab.

By ToolSite7 min readguides

Cron Shortcut Macros

Some cron implementations (Vixie cron, used on most Linux distributions) support named shortcuts. These replace the five-field expression with a single keyword:

| Shortcut | Equivalent | Meaning | |---|---|---| | @yearly | 0 0 1 1 * | Once a year at midnight Jan 1 | | @annually | 0 0 1 1 * | Same as @yearly | | @monthly | 0 0 1 * * | Once a month at midnight on the 1st | | @weekly | 0 0 * * 0 | Once a week at midnight Sunday | | @daily | 0 0 * * * | Once a day at midnight | | @midnight | 0 0 * * * | Same as @daily | | @hourly | 0 * * * * | Once an hour at minute 0 | | @reboot | N/A | Runs once at system startup |

These shortcuts are convenient but less common outside of system crontabs. Most CI/CD platforms (GitHub Actions, GitLab CI, Kubernetes CronJobs) don't support them. Stick to five-field expressions for portability across platforms.

The @reboot shortcut is unique. It doesn't correspond to any five-field expression because "at boot" isn't a time. It's a one-shot trigger that fires when the cron daemon starts, which typically happens at system boot. Use it for startup scripts that need to run after the system comes online: mounting network drives, starting services, or sending a "server is up" notification.

Every-N-Minute Patterns

| Expression | Meaning | |---|---| | */1 * * * * | Every minute | | */5 * * * * | Every 5 minutes | | */10 * * * * | Every 10 minutes | | */15 * * * * | Every 15 minutes | | */30 * * * * | Every 30 minutes |

Note: */5 in the minute field runs at 0, 5, 10, 15, ..., 55. It does not run at 1, 2, 3, 4. If you need an offset (e.g., every 5 minutes starting at minute 2), use a comma list: 2,7,12,17,22,... or use a wrapper script that sleeps for the offset.

Hourly Patterns

| Expression | Meaning | |---|---| | 0 * * * * | Every hour at minute 0 | | 30 * * * * | Every hour at half past | | 0 */2 * * * | Every 2 hours | | 0 */6 * * * | Every 6 hours | | 0 0,12 * * * | Twice a day at midnight and noon |

The */2 in the hour field runs at hours 0, 2, 4, 6, ..., 22. That's midnight, 2 AM, 4 AM, and so on. If you want "every 2 hours starting at 1 AM," use 0 1,3,5,7,9,11,13,15,17,19,21,23 * * *. The list is longer but exact.

Daily Patterns

| Expression | Meaning | |---|---| | 0 0 * * * | Midnight | | 0 6 * * * | 6:00 AM | | 30 4 * * * | 4:30 AM | | 0 9 * * 1-5 | 9:00 AM on weekdays | | 0 22 * * * | 10:00 PM |

Weekday ranges (1-5) mean Monday through Friday. Some systems treat 0 as Sunday and 7 also as Sunday, so 0-7 can mean Sunday through Sunday (all days). On most Linux systems, 0 = Sunday, 1 = Monday, ..., 6 = Saturday. Double-check your system's man page (man 5 crontab) if you're unsure.

Day-of-Week and Day-of-Month Interaction

When both day-of-month and day-of-week are specified (neither is *), the job runs when EITHER field matches. This is the "OR" rule:

0 0 1 * 1    Runs on the 1st of the month AND every Monday
0 0 * * 1    Runs only on Mondays (day-of-month is *)
0 0 1 * *    Runs only on the 1st (day-of-week is *)

This is the most common source of cron bugs. Someone writes 0 0 1 * 1 meaning "the first Monday of the month" and gets "every Monday plus the 1st of every month" instead. If you need "the 1st of the month only if it's a Monday," cron can't express that. Add the date check inside the script:

# At the top of your script
if [ "$(date +\%d)" != "01" ] || [ "$(date +\%u)" != "1" ]; then
    exit 0
fi

Weekly and Monthly Patterns

| Expression | Meaning | |---|---| | 0 0 * * 0 | Weekly on Sunday midnight | | 0 0 * * 1 | Weekly on Monday midnight | | 0 0 1 * * | Monthly on the 1st at midnight | | 0 0 15 * * | Monthly on the 15th at midnight | | 0 0 1 */3 * | Quarterly on the 1st | | 0 0 1 1 * | Once a year at midnight Jan 1 |

For "last day of the month," cron has no native support (some implementations support L but it's not portable). The common workaround:

0 0 28-31 * * [ "$(date -d tomorrow +\%d)" = "01" ] && /path/to/script

This runs on days 28-31 and the script checks if tomorrow is the 1st. If yes, today is the last day of the month.

Ranges and Lists

| Expression | Meaning | |---|---| | 0 9-17 * * * | Every hour from 9 AM to 5 PM | | 0 9-17 * * 1-5 | Every hour 9-5, Monday through Friday | | 0 0,12 * * * | Midnight and noon only | | 15,45 * * * * | At minutes 15 and 45 of every hour | | 0 0 1,15 * * | 1st and 15th of every month |

Ranges specify a continuous interval. Lists specify discrete values. You can combine them: 0 0 1,15,20-25 * * runs on the 1st, 15th, and every day from the 20th through the 25th.

Unusual but Useful Patterns

| Expression | Meaning | |---|---| | 0 9 1-7 * 1 | First Monday of the month at 9 AM | | 0 12 * * 3 | Every Wednesday at noon | | 0 0 28-31 * * | Runs on days 28-31 (for last-day checks) | | 0 8-18/2 * * * | Every 2 hours from 8 AM to 6 PM | | 0 0 * * 1,3,5 | Monday, Wednesday, Friday at midnight | | 59 23 31 12 * | One minute before the new year |

The "first Monday" pattern (0 9 1-7 * 1) works because it restricts day of month to 1-7 AND day of week to Monday (1). The only Monday that falls in days 1-7 is the first Monday of the month.

The "every 2 hours during business hours" pattern (0 8-18/2 * * *) combines a range with a step. It runs at 8, 10, 12, 14, 16, 18. Compare with 0 */2 8-18 * * * which also includes 8 and 18 but steps through the hour field differently.

The Seconds Field

Some implementations add a sixth field for seconds at the beginning: sec min hour dom month dow. Quartz Scheduler (Java), Spring @Scheduled, and AWS EventBridge use six-field expressions. A cron for "every 30 seconds" is */30 * * * * *. Standard five-field cron can't express sub-minute schedules. Always check your platform's documentation.

Cron vs Systemd Timers

On modern Linux with systemd, systemd timers offer randomized delay windows, persistent catch-up after reboot, and better logging through journald. But cron is still simpler for one-liners and available on every Unix, including macOS.

Try it yourself: open the Cron Generator and test any expression from this cheat sheet. The tool translates it to human-readable English so you can confirm the schedule is what you intended. Try the trickier patterns like 0 0 1-7 * 1 (first Monday) to see how the description helps catch mistakes.

Related Reading