Cron Expressions Explained: Syntax, Examples & Pitfalls

September 26, 2026 · DevOps

Five little fields, endless confusion: */15 9-17 * * 1-5 looks like line noise until you learn to read it — then it's obvious. Cron expressions schedule recurring jobs on virtually every Unix system and in countless schedulers (CI pipelines, cloud cron, application frameworks). This guide teaches you to read and write them confidently, including the two traps that bite everyone exactly once.

What a cron expression is and where you'll meet one

A cron expression is a compact schedule: "run this at these times". The cron daemon on Linux/Unix reads them from crontabs (per-user schedule files, edited with crontab -e). But the syntax escaped Unix long ago — you'll meet it in GitHub Actions (on.schedule), AWS EventBridge, Kubernetes CronJobs, Jenkins, and Node libraries like node-cron. Dialects vary slightly (more on that later), but the five-field core is universal.

The five fields, left to right

┌──────────── minute (0–59)
│ ┌────────── hour (0–23)
│ │ ┌──────── day of month (1–31)
│ │ │ ┌────── month (1–12)
│ │ │ │ ┌──── day of week (0–7, 0 and 7 = Sunday)
│ │ │ │ │
* * * * *

Read narrowest to widest: which minute, of which hour, on which day. Each field is a set of allowed values; the job runs when all fields match the current time (with one famous exception — keep reading).

Special characters: *, ,, -, /

CharMeaningExample
*Every possible value* * * * * — every minute
,List of values0 9,17 * * * — 9:00 and 17:00 daily
-Range0 9-17 * * * — hourly, 9am–5pm
/Step*/15 * * * * — every 15 minutes

They compose: 0 9-17/2 * * 1-5 means "every 2 hours from 9 to 17, Monday to Friday" — 9:00, 11:00, 13:00, 15:00, 17:00 on weekdays. Month and weekday fields also accept names (JAN–DEC, MON–SUN) in most implementations, which reads better than numbers.

Reading real examples

ExpressionMeaning
0 9 * * 1-59:00 every weekday
*/15 * * * *Every 15 minutes
0 0 1 * *Midnight on the 1st of each month
30 2 * * 02:30 every Sunday morning
0 8 * * 18:00 every Monday
45 23 * * 523:45 every Friday

Build it visually: describe your schedule in plain English with our free Cron Expression Generator and get the expression plus a human-readable explanation.

The two traps everyone hits once

Trap 1: 5 * * * * vs */5 * * * *. The first means "at minute 5 of every hour" — once per hour. The second means "every 5 minutes". If your "every 5 minutes" job mysteriously runs hourly, you wrote the first one. The */n step syntax is what divides the hour; a bare n is a single minute.

Trap 2: day-of-month OR day-of-week. When both day fields are restricted, cron runs when either matches — not both. 0 0 1 * 1 runs on the 1st of every month and every Monday. There is no standard-cron way to say "Monday the 1st"; you need the job itself to check the date, or a smarter scheduler. Tattoo this on your brain: days are OR, everything else is AND.

Special strings

StringEquivalent
@rebootRun once at startup
@hourly0 * * * *
@daily0 0 * * * (midnight)
@weekly0 0 * * 0
@monthly0 0 1 * *
@yearly0 0 1 1 *

These are conveniences, not magic — @daily is exactly 0 0 * * *. Note @reboot doesn't take fields at all; it's "run this command once when the daemon starts".

Dialects and gotchas

  • Quartz / Java schedulers use 6–7 fields (seconds first, optional year last): 0 */5 * * * ?. The ? means "no specific value" and exists to dodge the day-of-month/day-of-week OR trap.
  • AWS EventBridge / CloudWatch cron also has 6 fields and requires ? in one of the day fields.
  • Timezone: cron uses the system local timezone. A server in UTC running your "9am" job at 9am UTC is a classic incident. Some crons honor a TZ= line; otherwise do the conversion yourself.
  • The environment trap: cron runs with a minimal environment — tiny PATH, no shell profile, no aliases. A script that works in your terminal but fails in cron is almost always missing an absolute path or an env var. Always use full paths and redirect output to a log: */5 * * * * /usr/local/bin/job.sh >> /var/log/job.log 2>&1.
  • Overlapping runs: if a job takes longer than its interval, cron happily starts a second copy. Use a lock file (flock) for anything that must not overlap.

Frequently asked questions

What does */5 * * * * mean?
"Every 5 minutes." The */n syntax means "every nth unit" starting at 0 — so minutes 0, 5, 10, 15, and so on. Don't confuse it with `5 * * * *`, which means "at 5 minutes past every hour" (once per hour).
How do I run a cron job every 5 minutes?
Use `*/5 * * * *`. The step syntax in the minute field divides the hour into 5-minute slots. If you need it offset (e.g. at :2, :7, :12 past), write `2-57/5 * * * *`.
What are the 5 fields in cron?
Minute (0–59), hour (0–23), day of month (1–31), month (1–12), day of week (0–7, where both 0 and 7 mean Sunday). They're read left to right, narrowest time unit to widest.
Why did my cron job not run?
The usual suspects: the cron daemon isn't running or the crontab wasn't installed for the right user; the command relied on PATH or environment variables that don't exist in cron's minimal environment (use absolute paths); a typo in the schedule; or the script needs a shell profile that cron never loads. Redirect output to a log file so failures aren't silent.
What's the difference between day of month and day of week?
When both fields are restricted (not `*`), cron treats them as OR, not AND: `0 0 1 * 1` runs on the 1st of the month AND every Monday — not "Monday the 1st". This is the most misunderstood cron behavior there is.
Can cron run every second?
No — the smallest cron unit is one minute. For sub-minute scheduling, run a script every minute that loops internally, or use a proper scheduler (systemd timers support sub-minute accuracy, as do most job queues).
What timezone does cron use?
The system's local timezone by default — which is whatever the server was configured with, a classic source of "it ran at the wrong time" bugs. Some cron implementations allow a TZ variable in the crontab; otherwise, convert your intended time to the server's timezone explicitly.

Related articles

Try the free tool