Cron Expression Syntax Explained: A Practical Cheat Sheet

Learn how to read and write cron expressions with real examples — every 5 minutes, daily at midnight, weekdays only — plus the gotchas that trip up even senior developers.

Cron expressions are one of those things every developer half-remembers. You know it's five fields, you know * means "every", and then you end up googling "cron every 5 minutes" for the tenth time anyway.

This is the cheat sheet we wish existed: the syntax, the most common expressions ready to copy-paste, and the gotchas that cause jobs to fire at the wrong time (or not at all).

The anatomy of a cron expression

A standard cron expression has five fields, separated by spaces, read from left to right:

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

Each field accepts a few operators:

That's 95% of the syntax. Combine them and you can express almost any schedule.

Common cron expressions (copy-paste ready)

Every minute:

* * * * *

Every 5 minutes:

*/5 * * * *

Every hour, on the hour:

0 * * * *

Every day at midnight (a classic for backups):

0 0 * * *

Every day at 6:30 AM:

30 6 * * *

Every Monday at 9 AM (weekly reports):

0 9 * * 1

Weekdays only, at 8 AM:

0 8 * * 1-5

First day of every month at midnight (billing, invoicing):

0 0 1 * *

Every 15 minutes during business hours, weekdays:

*/15 9-17 * * 1-5

Twice a day, at 6 AM and 6 PM:

0 6,18 * * *

The gotchas that bite people

1. Day-of-month and day-of-week are OR, not AND

This is the most infamous cron quirk. Take this expression:

0 0 13 * 5

You might read it as "midnight on Friday the 13th". In standard cron it actually fires on the 13th of every month AND every Friday — the two day fields are combined with OR when both are restricted. If you need a true "Friday the 13th" job, check the weekday inside your script instead.

2. Cron runs in the server's timezone

0 9 * * * means 9 AM somewhere — and that somewhere is wherever your server thinks it is, which for most cloud VMs is UTC. Your "9 AM report" lands in inboxes at 4 AM in New York or at noon in Istanbul.

Worse, if your server's timezone observes daylight saving time, a job scheduled at 2:30 AM may run twice one night in autumn and not at all one night in spring.

The fix: schedule in UTC when you can, or use a scheduler that supports per-job timezones. CronSpark lets you attach an explicit timezone (like Europe/Istanbul or America/New_York) to every job, so "9 AM" means 9 AM for your users — DST handled for you.

3. */30 in the hour field doesn't mean "every 30 hours"

Steps operate within a field's range. */30 in the hour field (0–23) only matches hour 0 — so the job runs once a day at midnight, not every 30 hours. Cron simply can't express intervals that don't fit inside a single field. If you need "every 36 hours" or "every 10 seconds", you want an interval-based scheduler, not a cron expression.

4. A schedule is not a guarantee

The expression only says when the job should fire. If the server was rebooting at that moment, the cron daemon was misconfigured, or the script crashed halfway — cron won't tell you. The expression was "correct" and the job still didn't run.

That's why pairing schedules with monitoring matters: an external service that expects each run and alerts you when one goes missing. We wrote about that pattern in what is a dead man's switch monitor.

Don't want to memorize any of this?

Fair. Two shortcuts:

  1. Read expressions with a translator — tools like cronstrue turn */15 9-17 * * 1-5 into plain English.
  2. Build them visually — CronSpark has a visual cron builder that shows a live human-readable preview and the next five run times before you save a job, so you never deploy a schedule that fires at the wrong hour.

Summary

Five fields — minute, hour, day of month, month, day of week — plus four operators (*, ,, -, /) cover nearly every schedule you'll need. Watch for the OR behavior of the two day fields, be explicit about timezones, and remember that a cron expression only requests a run. To know your jobs actually ran, put a monitor like CronSpark behind them.