Cron Expression Cheat Sheet: 20 Schedules You Actually Need

Twenty cron expressions covering the schedules that actually come up, plus the field-order rules and the three mistakes that cause jobs to fire far more often than intended.

Cron Expression Cheat Sheet: 20 Schedules You Actually Need
Share this article:

Cron syntax is five fields and almost no forgiveness. Get one wrong and your job either never runs or runs 1,440 times a day. This is the reference I keep open, plus the reasoning behind the entries that are easy to get wrong.

The five fields

┌───────────── minute        (0 - 59)
│ ┌─────────── hour          (0 - 23)
│ │ ┌───────── day of month  (1 - 31)
│ │ │ ┌─────── month         (1 - 12)
│ │ │ │ ┌───── day of week   (0 - 6, Sunday = 0)
│ │ │ │ │
* * * * *  command

Four operators do almost everything:

  • `*` — every value
  • `,` — a list, as in `1,15,30`
  • `-` — a range, as in `9-17`
  • `/` — a step, as in `*/15`

The cheat sheet

ExpressionWhen it runs
`* * * * *`Every minute
`*/5 * * * *`Every 5 minutes
`*/15 * * * *`Every 15 minutes
`0 * * * *`Every hour, on the hour
`0 */2 * * *`Every 2 hours
`30 * * * *`Every hour at :30
`0 0 * * *`Every day at midnight
`0 9 * * *`Every day at 09:00
`0 9,17 * * *`Twice daily, 09:00 and 17:00
`0 9 * * 1-5`Weekdays at 09:00
`0 9 * * 6,0`Weekends at 09:00
`*/30 9-17 * * 1-5`Every 30 min, business hours, weekdays
`0 9 * * 1`Every Monday at 09:00
`0 0 1 * *`First day of every month
`0 0 1 1,4,7,10 *`Quarterly (1 Jan, Apr, Jul, Oct)
`0 0 1 1 *`Once a year, 1 January
`0 3 * * 0`Weekly maintenance, Sunday 03:00
`0 0 * * 1-5`Every weekday at midnight
`15 2 * * *`Daily at 02:15
`0 0 28-31 * *`Last-day-of-month candidates (see below)

You can build and verify any of these with the cron expression builder, which breaks an expression down field by field and shows the next run times.

The three mistakes that actually cause incidents

1. Using `*` where you meant `0`

This is the big one:

* 2 * * *     ← runs 60 times, every minute from 02:00 to 02:59
0 2 * * *     ← runs once, at 02:00

The minute field is not "unspecified", it is "every". A backup job written the first way runs sixty times a night. If you take one thing from this page, take this.

2. Assuming step values start where you think

`*/20` in the minute field fires at :00, :20 and :40 — it counts from zero, not from when you deployed. And steps do not wrap: `*/45` gives you :00 and :45, then jumps back to :00 the next hour, so the gap is 15 minutes, not 45. If you need a true 45-minute interval, cron cannot express it; use a scheduler with interval semantics instead.

3. Combining day-of-month and day-of-week

This one surprises nearly everyone:

0 0 1 * 1     ← NOT "the first of the month, if it's a Monday"
              ← it means "the 1st of the month, OR any Monday"

When both day fields are restricted, standard cron treats them as OR, not AND. That expression fires roughly five times a month, not once. If you need "first Monday of the month", cron cannot express it directly — run it every Monday and exit early in the script:

0 0 * * 1 [ "$(date +%d)" -le 07 ] && /path/to/job

Special strings

Most cron implementations accept these shorthands, and they are considerably easier to read at a glance:

ShorthandEquivalent
`@yearly` / `@annually``0 0 1 1 *`
`@monthly``0 0 1 * *`
`@weekly``0 0 * * 0`
`@daily` / `@midnight``0 0 * * *`
`@hourly``0 * * * *`
`@reboot`Once at startup

`@reboot` is the odd one out: it is not a schedule at all, and it does not run on a timer. It also does not fire on `systemctl restart cron`, only on actual boot, which catches people out.

Five or six fields?

Standard Unix cron uses five fields. Several other systems prepend a seconds field, giving six:

  • Quartz (Java), Spring `@Scheduled`, and AWS EventBridge use six or seven fields
  • Kubernetes CronJob and GitHub Actions use standard five
  • node-cron accepts both, which is its own source of confusion

So `0 0 12 * * ?` in Quartz means noon daily, while the same string in Unix cron is a syntax error. Always check which flavour your scheduler expects before copying an expression from a search result.

Frequently asked questions

What timezone does cron use?

The system timezone of the machine it runs on, unless the scheduler says otherwise. This causes real problems around daylight saving — a job scheduled at 02:30 can run twice or not at all on the changeover days. Worth understanding before you schedule anything financial.

How do I run something every 90 minutes?

You cannot, cleanly. Cron steps must divide evenly into the field. Use two entries (`0 0,3,6,9,12,15,18,21 * * *` for every 3 hours) or a scheduler with interval semantics.

Does `0 0 * * 7` work for Sunday?

In most implementations yes — both 0 and 7 mean Sunday. But it is not universal, so `0` is the safer choice.

Why did my job not run at all?

The usual causes are a missing newline at the end of the crontab file, a `PATH` that differs from your shell's, or an unescaped `%` (which cron treats as a newline). Use absolute paths in cron commands.

How do I check an expression without waiting?

Paste it into the [cron builder](/cron-expression-builder) and read the next scheduled run times.

Related reading

Scheduling and time handling go together. If your job logs Unix timestamps, the timestamp converter turns them into readable dates. If your team spans regions, the world clock and timezone meeting planner help work out what "03:00 UTC" means for everyone involved.

Build a cron expression →