Home · Blog · Cron Expressions
DevOps · Scheduling

Cron Expressions Explained, With Examples

Cron is small, weird and easy to get wrong. Here's the field layout, the special characters, the gotchas that bite in production, and a copy-paste table of the expressions you actually need.

8 min read·Updated June 2026

The five fields

 ┌───────── minute        (0–59)
 │ ┌─────── hour          (0–23)
 │ │ ┌───── day of month  (1–31)
 │ │ │ ┌─── month         (1–12 or JAN–DEC)
 │ │ │ │ ┌─ day of week   (0–6 or SUN–SAT; 7 = Sunday too)
 │ │ │ │ │
 *  *  *  *  *   command

Quartz/Spring add a seconds field at the start, making it six. Some implementations add a year field at the end. Always confirm which dialect your scheduler uses.

The special characters

The gotcha that catches everyone

In standard Unix cron, day-of-month and day-of-week are OR'd, not AND'd. If you write:

0 9 1 * MON   # 9am, on the 1st of the month, OR on Monday

…it fires every Monday and on the 1st of every month. To get "9am on the 1st only if it's a Monday", you need application-level logic — pure cron can't express it.

PitfallCombining day-of-month and day-of-week in the same expression almost never does what you expect. Pick one, leave the other as *.

Timezones

Linux cron uses the system timezone (often UTC on servers, local time on workstations). Managed schedulers are different:

Always write the timezone next to the expression in code comments. "Midnight" without a timezone is a future incident.

Reference table

The expressions you actually need:

* * * * *          every minute
*/5 * * * *        every 5 minutes
0 * * * *          top of every hour
0 */2 * * *        every 2 hours
30 9 * * *         every day at 09:30
0 9 * * 1-5        weekdays at 09:00
0 9 * * MON-FRI    (same, named form)
0 0 * * 0          every Sunday at midnight
0 0 1 * *          1st of every month at midnight
0 0 1 1 *          midnight on January 1st
15 10 * * 1        Mondays at 10:15
0 22 * * 1-5       weekdays at 22:00 (nightly job)
0 0 * * 6,0        every Saturday and Sunday at midnight
*/15 9-17 * * 1-5  every 15 min, business hours, weekdays

Common mistakes

  1. Wrong field count. Pasting a 6-field Quartz expression into Linux cron silently misinterprets the meaning.
  2. Forgotten timezone. A "daily report at 8am" runs at 3am for half the world.
  3. DST drift. An expression like 30 2 * * * either runs twice or skips entirely on DST transitions in local-time schedulers.
  4. Overlapping runs. A job that takes 6 minutes scheduled every 5 will pile up. Use a lockfile, or a scheduler with concurrencyPolicy: Forbid.
  5. Step ambiguity. */7 in the hour field doesn't fire every 7 hours — it fires at hours 0, 7, 14, and 21, then jumps back to 0. Use explicit lists for non-divisors.
TipAlways paste your expression into a generator and check the next 5–10 fire times before deploying. The Cron Generator does exactly that, in your browser.

FAQ

Why is my cron job not running at midnight on the 1st?
Day-of-month and day-of-week are OR'd, not AND'd, in standard cron. Setting both to non-* values fires more often than most people expect. Check your timezone too — server time may not be what you assume.
Does cron use UTC or local time?
Linux cron uses the system timezone. Managed schedulers like AWS EventBridge and Kubernetes CronJob (pre-1.27) default to UTC. Always document the timezone alongside the expression.
What's the difference between 5-field and 6-field cron?
5-field is the classic Unix layout: minute, hour, day-of-month, month, day-of-week. 6-field (Quartz, Spring, AWS EventBridge) adds a seconds field at the start or a year field at the end. Confirm which dialect your scheduler expects.
How do I run something every 30 seconds?
Standard cron can't — the smallest unit is a minute. Use a 6-field Quartz expression like */30 * * * * ?, or run a 1-minute cron that itself triggers two runs 30 seconds apart.