Back to blog

Crontab Syntax Explained: From Basics to Advanced Schedules

A practical guide to crontab syntax covering minute, hour, day, month, and weekday fields with real examples for common scheduling patterns.

CronGuard TeamCron Job Monitoring Experts
4 min read
Close-up of a Linux shell prompt on a black screen, with the command sudo partly typed and the cursor still blinking

The Five Fields

A crontab schedule has five fields separated by spaces:

* * * * * command
| | | | |
| | | | +-- Day of week (0-7, where 0 and 7 are Sunday)
| | | +---- Month (1-12)
| | +------ Day of month (1-31)
| +-------- Hour (0-23)
+---------- Minute (0-59)

Basic Patterns

Every Minute

* * * * * /path/to/script.sh

Runs every single minute. Useful for real-time monitoring but resource-intensive.

Every Hour

0 * * * * /path/to/script.sh

Runs at minute 0 of every hour.

Every Day at Midnight

0 0 * * * /path/to/script.sh

Every Monday at 9 AM

0 9 * * 1 /path/to/script.sh

First Day of Every Month at 6 AM

0 6 1 * * /path/to/script.sh

Special Characters

Comma: Multiple Values

# Run at 9 AM, 12 PM, and 5 PM
0 9,12,17 * * * /path/to/script.sh

Dash: Range

# Run every hour from 9 AM to 5 PM
0 9-17 * * * /path/to/script.sh

Slash: Step Values

# Run every 5 minutes
*/5 * * * * /path/to/script.sh

# Run every 2 hours
0 */2 * * * /path/to/script.sh

Real-World Examples

Database Backup - Daily at 2 AM

0 2 * * * /usr/local/bin/backup-db.sh

Log Rotation - Weekly on Sunday at 4 AM

0 4 * * 0 /usr/sbin/logrotate /etc/logrotate.conf

Health Check - Every 5 Minutes During Business Hours

*/5 9-17 * * 1-5 /path/to/health-check.sh

Monthly Report - First Monday of Every Month

This one is tricky. Cron does not natively support "first Monday." Use a wrapper:

# Run on the 1st through 7th, but only if it is Monday
0 9 1-7 * 1 /path/to/monthly-report.sh

Quarterly Cleanup - Jan, Apr, Jul, Oct

0 3 1 1,4,7,10 * /path/to/quarterly-cleanup.sh

Special Strings

Some cron implementations support shorthand strings:

@reboot    Run once at startup
@yearly    0 0 1 1 *
@monthly   0 0 1 * *
@weekly    0 0 * * 0
@daily     0 0 * * *
@hourly    0 * * * *

Common Mistakes

Forgetting Timezone

Cron uses the system timezone. If your server is UTC but you want jobs to run at 9 AM local time, you need to calculate the UTC equivalent. Or set the timezone in crontab:

CRON_TZ=America/New_York
0 9 * * * /path/to/script.sh

Day-of-Month AND Day-of-Week

When both day-of-month and day-of-week are set (not *), cron runs the job when either matches, not when both match. This surprises many people.

# Runs on the 15th AND every Friday, not "Friday the 15th"
0 9 15 * 5 /path/to/script.sh

Minute Field Wildcard

# BAD: runs every minute of every hour
* */2 * * * /path/to/script.sh

# GOOD: runs once every 2 hours at minute 0
0 */2 * * * /path/to/script.sh

Testing Your Crontab

Before deploying, verify your schedule does what you think it does. Online tools like crontab.guru let you paste a cron expression and see the next execution times.

And regardless of how correct your syntax is, monitor your cron jobs to confirm they actually run. A perfectly written crontab on a server that lost its cron daemon does nothing at all.

Frequently asked questions about crontab syntax

What do the five crontab fields mean? A crontab schedule has five fields separated by spaces, and they run left to right: minute (0-59), hour (0-23), day of month (1-31), month (1-12), and day of week (0-7, where both 0 and 7 mean Sunday). Everything after those five fields is the command to run, so a line of five asterisks followed by a command runs that command every single minute.

How do I run a job every few minutes or every few hours? Use the slash character for step values: a schedule of star-slash-5 in the minute field runs every 5 minutes, and 0 with star-slash-2 in the hour field runs every 2 hours. The common mistake here is leaving the minute field as a plain asterisk while stepping the hour field, because that runs the job every minute of every other hour rather than once every 2 hours. Put 0 in the minute field when you want a single run per interval.

Why does my job run on both the 15th and every Friday? Because when both day-of-month and day-of-week are set to something other than an asterisk, cron runs the job when either field matches, not when both match. A schedule of 0 9 15 * 5 therefore runs on the 15th and on every Friday, not on "Friday the 15th." This surprises many people.

How do I make cron run a job in a specific timezone? Cron uses the system timezone by default, so if your server is UTC but you want a job to run at 9 AM local time, you either calculate the UTC equivalent yourself or set the timezone in the crontab with a CRON_TZ line above the schedule.

Can cron schedule something like the first Monday of every month? Not natively, so you need a wrapper around it. The usual trick is to schedule the job for the 1st through the 7th of the month and also restrict it to Monday, which means it fires only on the one day in that window that is a Monday. Before deploying any schedule, verify it does what you think: online tools like crontab.guru let you paste a cron expression and see the next execution times.

Further reading


Conclusion: Crontab syntax is simple but has sharp edges. The day-of-month/day-of-week OR behavior, timezone assumptions, and minute-field wildcards trip up even experienced engineers. Use shorthand strings when possible, test expressions before deploying, and always monitor that jobs actually execute.

Sources: crontab(5) manual.

Share

Related posts

A dark server rack lit green from within, showing rows of patch-panel ports with black and colored network cables looped between them, and a blurred second rack on the left
ReliabilityDurable Job Queues Are Replacing Cron: New Failure Modes
Close-up of a high-density network switch panel with rows of small ports, several aqua fiber-optic cables plugged in, and orange status lights glowing along the rows
ReliabilityTime Zone Changes Are Moving When Your Cron Jobs Run
A monitor in a dark room showing a Linux terminal with neofetch output for a Debian system: kernel version, uptime, package count, shell and CPU details next to the Debian swirl in ASCII art
DevOpsCron Alternatives Compared: systemd Timers, Celery Beat, and More

Set up your first monitor.
It'll take 30 seconds.