Cron Expressions Explained

A cron expression is a five-field string — minute, hour, day of month, month, day of week — that tells a scheduler when to run a job. The same syntax drives Linux crontab, Kubernetes CronJobs, GitHub Actions schedules, Cloudflare Workers cron triggers and most CI systems, and six-field variants such as Quartz and AWS EventBridge build on it, so reading and writing it is a skill you use everywhere.

This guide walks through each field and operator, gives the schedules people search for most, and explains the traps — time zones, day-of-month versus day-of-week, and the extra seconds field in some systems — that cause a job to fire at the wrong time or not at all.

The five fields

A cron expression is five fields read by position, left to right: minute, hour, day of month, month and day of week; * leaves its field unrestricted.

At 09:00 Monday to Friday

Part 1:0 Part 2:9 Part 3:* Part 4:* Part 5:1-5

At 02:30 every day

Part 1:30 Part 2:2 Part 3:* Part 4:* Part 5:*

Every minute

Part 1:* Part 2:* Part 3:* Part 4:* Part 5:*

  1. 1MinuteAllowed: 0–590: at minute 0
  2. 2HourAllowed: 0–239: at hour 9
  3. 3Day of monthAllowed: 1–31*: any day of the month
  4. 4MonthAllowed: 1–12 or JAN–DEC*: any month
  5. 5Day of weekAllowed: 0–6 or SUN–SAT; Sunday = 0, and most implementations also accept 71-5: Monday to Friday

When both day fields are restricted, standard cron runs the job if either matches (OR); see “Day of month vs day of week”.

Text version
┌───────────── minute        (0–59)
│ ┌─────────── hour          (0–23)
│ │ ┌───────── day of month  (1–31)
│ │ │ ┌─────── month         (1–12 or JAN–DEC)
│ │ │ │ ┌───── day of week   (0–6, Sunday = 0, or SUN–SAT)
│ │ │ │ │
* * * * *   → every minute
30 2 * * *  → at 02:30 every day
0 9 * * 1-5 → at 09:00 Monday to Friday

Try it: Cron Expression Explainer

The operators: *, /, - and ,

  • * (asterisk) — every value. * in the hour field means every hour.
  • , (comma) — a list. 0,30 in the minute field means at minute 0 and minute 30.
  • - (hyphen) — a range. 9-17 in the hour field means 9, 10, … 17.
  • / (slash) — a step. */15 in the minute field means every 15 minutes (0, 15, 30, 45). In the day-of-month field, 1-31/2 (or */2) means the odd-numbered days — 1, 3, 5 … 31 — so a job that runs on the 31st runs again on the 1st: not a strict every-other-day interval.
  • Names — JAN–DEC and SUN–SAT are accepted in most implementations and are easier to read than numbers.
  • Extensions — some schedulers accept L (last), W (nearest weekday), # (nth weekday) and ? — these are Quartz/Spring features, not standard cron.

Try it: Cron Expression Generator

Common schedules

*/5 * * * *      every 5 minutes
0 * * * *        every hour, on the hour
0 */6 * * *      every 6 hours (00:00, 06:00, 12:00, 18:00)
0 0 * * *        every day at midnight
0 9 * * 1-5      weekdays at 09:00
0 9 * * 1        every Monday at 09:00
0 0 1 * *        first day of every month at midnight
0 0 1 1 *        once a year, 1 January
30 23 * * 0      Sundays at 23:30
0 8,20 * * *     twice a day, 08:00 and 20:00
15 3 */2 * *     odd-numbered days of the month at 03:15
0 0 * * 5        every Friday at midnight

Try it: Cron Expression Generator Try it: Cron Expression Validator

Time zones: the most common cron bug

A cron expression has no time zone of its own; it runs in whatever zone the scheduler uses. Linux crontab uses the system zone; Cloudflare Workers use UTC; GitHub Actions uses UTC unless the schedule sets a timezone; a Kubernetes CronJob uses the kube-controller-manager's local zone (often UTC on managed clusters) unless it sets .spec.timeZone; and cloud services vary. A job meant for 09:00 Riyadh time (UTC+3) must be written as 0 6 * * * on a UTC scheduler — and 09:00 in a zone that observes daylight saving shifts by an hour twice a year. Always document the zone next to the expression and convert explicitly.

Try it: Timezone Converter

Day of month vs day of week

When both day fields are restricted, standard cron runs the job if either matches (OR), not both. 0 0 13 * 5 fires on every 13th and on every Friday, not only on Friday the 13th. To express "second Tuesday of the month" or "last day of the month" you need Quartz-style extensions or a small check inside the job itself.

Try it: Cron Expression Explainer

Six-field cron and other variants

  • Quartz, Spring and some Java schedulers put a seconds field first: 0 */5 * * * ? — six or seven fields. Pasting a five-field expression there shifts every field by one.
  • AWS EventBridge scheduled rules use six fields — minute, hour, day of month, month, day of week and a year field — and require ? in one of the two day fields, with day of week counted 1–7 from Sunday: cron(0 9 ? * MON-FRI *). A five-field expression is rejected there.
  • Cloudflare Workers, Kubernetes, GitHub Actions and Vercel use the standard five fields.
  • Some systems accept @hourly, @daily, @weekly, @monthly and @yearly as shorthand.
  • Minimum resolution is one minute; sub-minute schedules need a different mechanism.

Try it: Cron Expression Validator

Testing a cron expression

Before deploying, paste the expression into a validator that lists the next several run times in your zone. Check that the first run is when you expect, that the interval between runs is right, and that month-end and weekend cases behave. Then make the job itself idempotent — safe to run twice — because every scheduler will eventually run it twice or skip it once.

Try it: Cron Expression Validator Try it: Unix Timestamp Converter

Frequently asked questions

What does * * * * * mean in cron?

Every minute of every hour of every day. Each asterisk means "every value" for its field.

How do I run a cron job every 5 minutes?

Use */5 * * * *. The step operator in the minute field fires at 0, 5, 10 … 55.

How do I schedule a job every Monday at 9 am?

0 9 * * 1 — minute 0, hour 9, any day of month, any month, weekday 1 (Monday).

Is Sunday 0 or 7 in cron?

Both work in most implementations: 0 and 7 mean Sunday. Use SUN if the scheduler accepts names, to avoid the ambiguity.

Which time zone does cron use?

Whatever the scheduler is configured with — the system zone for crontab, UTC for Cloudflare Workers, UTC for GitHub Actions unless the schedule sets a timezone, and the kube-controller-manager's zone for Kubernetes unless the CronJob sets .spec.timeZone. Convert your local time to that zone when writing the expression.

Why does my cron job not run?

The usual causes: the expression is in the wrong zone, a six-field expression was given to a five-field scheduler (or vice versa), the day-of-month and day-of-week fields combine as OR, or the job runs but fails silently — check the scheduler's logs and the next-run preview.

References

Tools mentioned in this guide

Convert a date and time between timezones (Riyadh, Amman, Delhi, UTC, New York, London…).

Utilities

More guides

More guides