A single line like 0 3 * * 1-5 expresses “run at 3:00 AM on weekdays”—that is the power of a Cron expression. From Linux crontab to GitHub Actions, Kubernetes CronJob, and most cloud schedulers, the world of scheduled execution runs on this syntax.

Mastering Cron Expressions: The 5 Fields and Special Characters with Examples

This article breaks down the five fields and special characters of Cron, pairing each expression with what it actually means. At the end you’ll find common pitfalls and an FAQ.

The Basic Structure: Five Fields

A standard crontab expression consists of five fields separated by spaces.

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

The valid range for each field is as follows.

FieldRangeNotes
Minute0–59
Hour0–230 = midnight, 23 = 11 PM
Day of month1–31
Month1–12JANDEC also valid (depends on implementation)
Day of week0–70 and 7 = Sunday, 1 = Monday; SUNSAT also valid

Note: GitHub Actions and Kubernetes use this 5-field format. Quartz and AWS EventBridge add “seconds” and/or “year” fields (6–7 fields), which require special characters like ? and L described below.

Understanding Special Characters by Example

Cron’s expressiveness comes from the special characters allowed in each field.

SymbolMeaningExampleReads as
*every value* * * * *every minute
,list of values0 9,12,18 * * *daily at 9, 12, and 18
-range0 9-17 * * *daily, on the hour from 9 to 17
/step interval*/15 * * * *every 15 minutes
?no specific value (day/weekday)0 0 ? * MONevery Monday at 0:00
Llast0 0 L * *last day of every month at 0:00
Wnearest weekday0 0 15W * *the weekday nearest the 15th
#nth weekday0 9 ? * MON#1first Monday at 9:00

Note that ?, L, W, and # are extensions of Quartz-style schedulers (Spring, Jenkins, EventBridge) and are not available in standard crontab.

Reading the / (step)

*/15 means “start at 0, step by 15,” i.e. 0,15,30,45. Combined with a range, you can set the starting point.

*/15 * * * *     → minutes 0,15,30,45 (every 15 minutes)
5/10 * * * *     → minutes 5,15,25,35,45,55 (every 10 min from 5)
0 9-17/2 * * *   → hours 9,11,13,15,17 (every 2 hours, 9 to 17)

Common Scheduling Examples

Copy-paste patterns that come up often in practice.

What you wantCron expression
Every minute* * * * *
Every 5 minutes*/5 * * * *
Every hour (on the hour)0 * * * *
Daily at midnight0 0 * * *
Daily at 9:30 AM30 9 * * *
Weekdays at 3 AM0 3 * * 1-5
Every 30 min, 9–18 on weekdays*/30 9-18 * * 1-5
Every Monday at 9 AM0 9 * * 1
First day of month at 0:000 0 1 * *
Last day of month (Quartz)0 0 L * *
First Monday at 9 AM (Quartz)0 9 ? * MON#1

Common Pitfalls

1. Specifying both day-of-month and day-of-week is an OR condition

0 0 1 * 1 means “midnight on the 1st OR on Monday”—not “when the 1st is a Monday.” This is counterintuitive, so specify only one and leave the other as * (or ? in Quartz).

2. Timezone depends on the runtime environment

Many cron daemons use the server’s timezone, while containers and CI commonly run in UTC. 0 0 * * * may fire at UTC midnight rather than your local midnight. If you want 2 AM in JST (UTC+9), the UTC expression is 0 17 * * *.

3. Leaving the minute field as * runs it every minute

* 3 * * * (intending “once at 3 AM”) actually runs 60 times during the 3 AM hour. For “once, at 3:00 sharp,” use 0 3 * * *.

4. Overlapping runs with short intervals

If a job scheduled every 5 minutes takes longer than 5 minutes, the next run starts before the previous finishes, causing overlap. Allow margin in the interval or add a locking mechanism.

Remove the “pre-save anxiety” with a tool

Cron expressions take practice to read and write, and the pitfalls above are hard to spot by eye. The Cron Expression Parser translates your expression into plain language and simulates the next run times, so you can verify the schedule at a glance before deploying.

Frequently Asked Questions

Is Sunday 0 or 7 in a Cron expression?

Most implementations accept both 0 and 7 for Sunday. Monday is 1 and Saturday is 6. To avoid confusion, use the three-letter names (SUN, MON, …) where supported.

How do I express “the last day of the month”?

In environments that support L (Quartz, EventBridge), use 0 0 L * *. Standard crontab has no L, so you need a workaround: run on candidate end-of-month days (28–31) and check the date in your script, or compute it programmatically.

How do I handle the difference between UTC and local time?

Subtract your UTC offset from the hour field. For JST (UTC+9), JST 2 AM becomes UTC 5 PM the previous day, i.e. 0 17 * * *. If your scheduler supports an explicit timezone setting, that is the most reliable option.

Are */5 and 0/5 the same?

In the minute field they are effectively identical (“every 5 minutes”: 0,5,10,…). */5 means “the whole range, stepping by 5,” while 0/5 means “from 0, stepping by 5.” The explicit start form helps when you want to offset the start (e.g. 7/15).