712Tools
5 min read

Cron expressions explained (for people who forget them every time)

The five fields, the special characters, and the schedules everyone actually uses. A field guide for anyone who Googles 'cron every 5 minutes' more than once a month.

The five fields

Standard Unix cron is five space-separated fields, in this order:

* * * * *
| | | | |
| | | | +-- day of week   (0-6, Sunday = 0)
| | | +---- month         (1-12)
| | +------ day of month  (1-31)
| +-------- hour          (0-23)
+---------- minute        (0-59)

Each field accepts:

  • A single number โ€” 5 means "5" (5 minutes past the hour if in the minute field).
  • A range โ€” 1-5 means "1, 2, 3, 4, 5".
  • A list โ€” 1,3,5 means "1 or 3 or 5".
  • A step โ€” */15 means "every 15" (from 0). 10-30/5 means "every 5 within 10-30".
  • A wildcard โ€” * means "every value".

That's the entire grammar. Everything else is convenience.

The schedules everyone actually uses

Bookmark this table and you'll never Google "cron every 5 minutes" again:

  • * * * * * โ€” every minute.
  • */5 * * * * โ€” every 5 minutes.
  • 0 * * * * โ€” every hour, on the hour.
  • 0 */3 * * * โ€” every 3 hours.
  • 0 0 * * * โ€” midnight every day.
  • 0 9 * * 1-5 โ€” 9 AM every weekday.
  • 0 9 * * 1 โ€” 9 AM every Monday.
  • 30 3 * * 0 โ€” 3:30 AM every Sunday.
  • 0 0 1 * * โ€” midnight on the 1st of every month.
  • 0 0 1 1 * โ€” midnight on January 1st (annually).

The special strings

Most cron implementations accept these shortcuts:

  • @yearly (or @annually) โ†’ 0 0 1 1 *
  • @monthly โ†’ 0 0 1 * *
  • @weekly โ†’ 0 0 * * 0
  • @daily (or @midnight) โ†’ 0 0 * * *
  • @hourly โ†’ 0 * * * *
  • @reboot โ€” run once at boot (event-triggered, not a schedule).

Portable across most Linux distros. Not supported by every cron implementation (BusyBox cron on Alpine, for instance, only supports @reboot).

The gotchas that cost real time

Timezones. Your server's crontab runs in the server's timezone. If your server is in UTC and you want a job at 9 AM Eastern, use 0 14 * * * (or 0 13 * * * in daylight saving time โ€” which is why "9 AM Eastern" schedules break twice a year). Better: set CRON_TZ=America/New_York at the top of the crontab if your cron supports it.

Day-of-month and day-of-week are OR'd. 0 0 1 * 1 runs at midnight on either the 1st of any month OR every Monday โ€” not "midnight on the 1st only if it's a Monday". If you want an AND, use two separate cron entries or check the day inside your script.

Environment variables. Cron runs jobs with a stripped-down environment. PATH is often just /usr/bin:/bin. Your script that works interactively may fail from cron because node, python, or aws aren't on the path. Fix: use absolute paths, or set PATH at the top of the crontab.

Output silently emailed (or lost). By default cron mails stdout/stderr to the local user. On modern servers with no mail daemon, the output vanishes. Redirect: 0 * * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1.

Overlapping runs. Nothing prevents a slow job from starting again while the previous run is still going. Wrap with flock: 0 * * * * flock -n /tmp/backup.lock /usr/local/bin/backup.sh.

Quartz cron: don't confuse it with Unix cron

Java's Quartz scheduler, Spring's @Scheduled, and AWS EventBridge use a 6- or 7-field cron with seconds and years. Quartz also supports ?, L (last day of the month), W (nearest weekday), and # (nth day of the month). None of these work in standard Unix cron.

If you're setting up a Kubernetes CronJob, Linux crontab, or GitHub Actions schedule: 5 fields, no seconds. If you're setting up Spring, Quartz, or AWS EventBridge: 6-7 fields with seconds.

Cron Generator targets standard 5-field Unix cron, since that's what 90% of scheduling systems use.

The verification workflow

Building a cron expression is a two-step debug cycle:

  1. Write the expression โ€” pick minute, hour, day, etc.
  2. Verify the next N run times match what you meant.

Step 2 is the one everyone skips. It's also the one that catches "wait, I wrote day-of-week when I meant day-of-month" before it hits production.

Cron Generator shows a plain-English translation and the next 5 run times as you build the expression. Paste an existing crontab entry to explain it โ€” often faster than re-deriving what past-you meant.

Related tools:

  • Regex Tester โ€” when your cron job is grep-ing log files and you need to check the pattern.
  • JSON Formatter โ€” when the job's output is JSON and you need to eyeball it.

Tools mentioned in this post