Cron Syntax Explained: Mastering the 5 Fields

Cron's five-field syntax is deceptively simple but incredibly powerful. This guide breaks down each field, every special character, and the rules that govern how they combine to create schedules ranging from 'every minute' to 'second Tuesday of odd-numbered months at 3:47 AM.'

The Five Fields

Every cron expression consists of exactly five fields in a specific order: minute hour day-of-month month day-of-week. Understanding what each field controls and its valid range is essential for constructing correct expressions.

The minute field (0-59) controls which minute of the hour the job runs. "0" means the top of the hour (:00), "30" means half past (:30), and "*" means every minute. This is the most granular control cron provides—you cannot schedule jobs at sub-minute intervals with standard cron.

The hour field (0-23) uses 24-hour time. "0" is midnight, "12" is noon, and "23" is 11 PM. There's no AM/PM notation; cron only understands 24-hour format. When you want 2 PM, use "14". When you want 2 AM, use "2" (or "02", leading zeros are optional and ignored).

The day of month field (1-31) specifies which day(s) of the month to run. "1" is the first of the month, "15" is the middle, and "31" is the last day for months that have 31 days. Be cautious with dates 29-31, as not all months have these days. A job scheduled for the 31st won't run in months with only 30 days.

The month field (1-12) controls which months run the job. "1" is January, "12" is December. Some cron implementations accept three-letter month abbreviations like "jan" or "JAN", but numeric values are universally supported and recommended for portability.

The day of week field (0-6 or 1-7, depending on implementation) specifies which days of the week to run. Standard cron uses 0 for Sunday, 1 for Monday, through 6 for Saturday. Some implementations also accept 7 for Sunday, making both 0 and 7 valid for Sunday. Like months, some cron variants accept abbreviations (sun, mon, tue, etc.), but numeric values are safest.

Understanding how these fields interact is crucial. When both day of month and day of week are specified (not wildcards), the job runs when either condition is true (OR logic, not AND). This trips up many users who expect "0 0 13 * 5" to run "on Friday the 13th" when it actually runs "on the 13th of every month OR every Friday."

Special Characters and Operators

Cron's power comes from special characters that let you express complex schedules concisely. The asterisk (*) means "every possible value" and is the most common special character. In the minute field, * means every minute (0-59). In the month field, * means every month (1-12).

The comma (,) operator lists multiple specific values. "0,15,30,45" in the minute field runs at :00, :15, :30, and :45 past each hour. "1,15" in the day of month field runs on the 1st and 15th. You can list as many values as needed: "1,3,5,7,9,11" for odd-numbered days.

The hyphen (-) creates ranges of consecutive values. "1-5" in the weekday field means Monday through Friday. "9-17" in the hour field means 9 AM through 5 PM. Ranges are inclusive—both endpoints are included. "1-3" means 1, 2, and 3.

The slash (/) creates step values or intervals. The format is "*/N" or "range/N". "*/5" in the minute field means "every 5 minutes" (0, 5, 10, 15, ..., 55). "*/2" in the hour field means "every 2 hours" (0, 2, 4, 6, ..., 22). You can combine steps with ranges: "10-50/5" means 10, 15, 20, 25, 30, 35, 40, 45, 50.

These operators can combine in a single field. "1-5,10,15,20-25" is valid, specifying 1 through 5, 10, 15, and 20 through 25. "*/10,55" means every 10 minutes plus specifically at :55 (so 0, 10, 20, 30, 40, 50, 55).

Some extended cron implementations support additional special characters. "L" (last) in day of month means the last day of the month. "W" (weekday) finds the nearest weekday to a given date. "#" specifies the Nth occurrence of a day (like "2nd Tuesday"). These extensions are powerful but not universally supported—standard cron doesn't include them.

The question mark (?) appears in some cron implementations as an alias for "*" in day of month or day of week fields. It exists primarily for clarity: using "?" in one field and a specific value in the other makes it clear you're only constraining one day-type field. Standard cron doesn't support "?"; use "*" instead.

Understanding operator precedence and combination rules prevents syntax errors. Ranges must be ascending (1-5, not 5-1). Step values must be positive. Lists should have values within the field's valid range. Most cron implementations don't validate your syntax until execution, so testing is crucial.

Common Syntax Patterns

Mastering a few common syntax patterns covers the majority of real-world scheduling needs. These building blocks can be combined and adapted for almost any schedule.

For simple regular intervals, use step syntax: "*/N * * * *" runs every N minutes. "0 */N * * *" runs every N hours (at minute 0). This pattern is common for monitoring, polling APIs, or periodic cleanup tasks.

For daily jobs at specific times, use "M H * * *" where M is the minute and H is the hour. "0 2 * * *" runs at 2:00 AM daily. "30 14 * * *" runs at 2:30 PM daily. The wildcards in month and both day fields mean "every day."

For weekday-only jobs, use "M H * * 1-5". "0 9 * * 1-5" runs at 9 AM Monday through Friday. This pattern is essential for business-day-only tasks like sending weekday reports or running integrations with external business systems that only operate on weekdays.

For specific days of the week, use "M H * * D" where D is 0-6. "0 0 * * 0" runs Sundays at midnight. "0 18 * * 5" runs Fridays at 6 PM. Multiple days work too: "0 8 * * 1,3,5" runs Monday, Wednesday, and Friday at 8 AM.

For monthly jobs, use "M H D * *" where D is the day of month. "0 0 1 * *" runs on the first of every month at midnight. "0 12 15 * *" runs on the 15th of every month at noon, perfect for mid-month processing.

For specific months or seasons, add month specification: "M H D M *". "0 0 1 1 *" runs annually on January 1st. "0 0 1 1,4,7,10 *" runs quarterly. "0 0 1 6-8 *" runs on the first of June, July, and August (summer months).

For complex schedules, combine operators creatively. "0 9-17/2 * * 1-5" runs at 9 AM, 11 AM, 1 PM, 3 PM, and 5 PM on weekdays—every 2 hours during business hours. "*/15 9-17 * * 1-5" runs every 15 minutes but only during business hours on weekdays.

When you need a schedule that doesn't fit these patterns, break it down into components. Think about when it should run (time), how often (interval), which days (day of week), which dates (day of month), and which months (month). Then construct each field according to those constraints.

Try the Tool

Crontab Generator

Crontab Generator