What a Cron Expression Actually Does
If you have ever pasted 0 3 * into a scheduler and hoped for the best, you are not alone. A cron expression is a compact string that tells a computer when to run a job, and its five-field syntax is unforgiving: one wrong character and your task runs at the wrong time, or never. This guide explains every field, walks through common examples, and shows you how to build and check an expression before you deploy it.
Cron has been part of Unix-like systems for decades, and the same five-field format now appears in container schedulers, CI pipelines, cloud job runners and application frameworks. The vocabulary is small. The mistakes are predictable. Once you understand the field order and the operators, you can read almost any schedule at a glance.
The Five Fields of a Cron Expression
A standard cron expression has five fields separated by spaces. Each field describes one unit of time.
- Minute (0-59)
- Hour (0-23, using a 24-hour clock)
- Day of month (1-31)
- Month (1-12, or three-letter names such as
JAN) - Day of week (0-7, where both 0 and 7 mean Sunday, or names such as
MON)
Read left to right, the string answers five questions: which minute, which hour, which day of the month, which month, and which day of the week. If a field is *, it means "every value in this range."
This is the single most important thing to internalise: the day-of-month and day-of-week fields are not combined with AND. When both are restricted, most implementations treat them as OR. That quirk causes more confusion than any other part of the syntax. If you write 0 0 1 MON, you are usually asking for midnight on the 1st of the month or* on any Monday, not midnight on a Monday that happens to fall on the 1st.
Field order matters more than field values
A schedule written as 30 9 1-5 runs at 09:30 on weekdays. Swap the first two numbers and you get 09:30 in the morning on the wrong day, or an invalid expression. Always read your string back to yourself as a sentence before you save it.
Cron Syntax Operators Explained
Six special characters do all the heavy lifting. Learn them once and the rest is arithmetic.
| Operator | Meaning | Example |
|---|---|---|
* | Any value | * in the hour field means every hour |
, | List of values | 1,15 means the 1st and 15th |
- | Range | 9-17 means 9 through 17 |
/ | Step | */15 means every 15 units |
? | No specific value | Used in some schedulers for day fields |
L, W, # | Last, nearest weekday, nth weekday | Extended syntax, not universal |
The step operator is the one people underuse. /5 in the minute field means every five minutes. 0 /2 means every two hours on the hour. You can also combine a range with a step: 0 9-17/2 fires at 09:00, 11:00, 13:00, 15:00 and 17:00.
Extended tokens such asL(last day),W(nearest weekday) and#(nth weekday of the month) are supported by some schedulers and rejected by others. Check your platform's documentation before relying on them.
How to Write a Cron Expression Step by Step
Follow this sequence and you will rarely ship a broken schedule.
- Write the schedule as a plain sentence first. "Every weekday at 07:15" is easier to reason about than a string of symbols.
- Map each part of the sentence to a field. 07:15 gives minute
15and hour7. "Every weekday" gives day of week1-5and*for the other two fields. - Assemble the five fields in order. You get
15 7 1-5. - Decide whether you need day-of-month at all. If you are scheduling by weekday, leave day of month as
*to avoid the OR behaviour described above. - Check the time zone. Cron runs in the system's local time unless you configure otherwise. A job that must run at a fixed moment across regions needs an explicit zone setting.
- Validate before deploying. Paste the expression into a parser to confirm it is syntactically valid and to see the next few run times.
Step 6 is where a free browser-based checker saves you time. A cron expression parser and validator lets you paste a string and read back the next scheduled runs without installing anything or sending your schedule to a server. Treat it as a syntax check, not a guarantee: it confirms the expression is valid and shows the times your system will compute, but it cannot know your server's clock settings or whether your scheduler supports extended tokens.
Common mistakes to avoid
- Using
0for Sunday when your platform expects7, or the reverse. - Forgetting that months and days use 1-based numbering while day of week often starts at 0.
- Assuming
*/30in the minute field means "at :00 and :30" — it does, but only because 60 divides evenly. - Writing a six-field expression for a five-field scheduler. The extra field is usually seconds, and it will be rejected.
Common Cron Expression Examples
These cover the majority of everyday scheduling needs.
| Schedule | Expression |
|---|---|
| Every minute | * |
| Every 15 minutes | /15 * |
| Every day at midnight | 0 0 * |
| Every day at 03:30 | 30 3 * |
| Every Monday at 09:00 | 0 9 1 |
| Weekdays at 08:00 | 0 8 1-5 |
| First day of the month at noon | 0 12 1 |
| Every six hours | 0 /6 |
| Every Sunday at 23:00 | 0 23 0 |
| Quarterly, first day at 02:00 | 0 2 1 1,4,7,10 * |
The last row shows how a list in the month field handles quarterly jobs. If you prefer named months, 0 2 1 JAN,APR,JUL,OCT * is equivalent on schedulers that accept names.
How Do You Schedule a Job to Run Every Weekday?
Use 0 8 1-5 for 08:00 Monday through Friday. The minute field holds 0, the hour field holds 8, and 1-5 restricts the day-of-week field to Monday through Friday. Leave day of month and month as * so the weekday rule is the only constraint. Adjust the hour and minute to your preferred time.
That is a 46-word answer you can adapt directly. The key detail is the untouched * in the day-of-month field. Restricting both day fields is where weekday schedules usually break.
Scheduling monthly and quarterly jobs
Monthly jobs need care around short months. 0 4 31 runs only in months that have a 31st, which means it skips February, April, June, September and November entirely. If you need "the last day of every month," you need the L token or a workaround, and both depend on your scheduler.
For quarterly work, a list in the month field is the clearest option. 0 2 1 1,4,7,10 * runs at 02:00 on the first day of January, April, July and October.
Handling time zones and daylight saving
Cron itself has no concept of time zones. The scheduler decides. When clocks move forward, jobs scheduled in the skipped hour may not run at all. When clocks move back, a job in the repeated hour may run twice.
If a job must fire at a fixed absolute time, check whether your platform supports a time-zone setting. If it does not, schedule around the transition or accept that twice-yearly drift is part of the deal.
Testing and Validating Before You Deploy
Reading an expression and trusting it is a poor strategy. Validate it instead.
- Confirm the field count matches what your scheduler expects.
- Check each value against its valid range.
- Walk through the next five run times by hand for anything important.
- Run the expression in a staging environment before it touches production.
A cron schedule checker in the browser gives you the next run times instantly, which is faster than reasoning through a step operator in your head. It will not tell you whether your job will succeed, only when it will be triggered. That distinction matters when you are debugging a job that runs on time and fails anyway.
Frequently Asked Questions
What does /5 * mean?
It means every five minutes, all day, every day. The */5 in the minute field steps through 0, 5, 10, 15 and so on up to 55, then repeats. The remaining fields are wildcards, so no hour, day or month restriction applies.
Can I run a cron job every 30 seconds?
Not with a standard five-field expression, because the smallest unit is one minute. Some schedulers accept a sixth field for seconds, which would let you write a sub-minute schedule. If yours does not, run the job twice per minute from a wrapper script or accept a one-minute interval.
Why did my job run on the wrong day?
The most likely cause is the day-of-month and day-of-week OR behaviour. If both fields are restricted, most implementations match either condition rather than both. Leave one of them as * unless you specifically want the OR result. A time-zone mismatch is the second most common cause.
Is cron syntax the same everywhere?
No. The five core fields are consistent across most implementations, but extended tokens such as L, W and #, named months, and seconds fields vary. The safest approach is to stick to the five standard fields and verify anything unusual against your platform's documentation.
Do I need to escape special characters?
In most schedulers you do not. The operators *, ,, - and / are part of the syntax and are parsed directly. Trouble usually appears when the expression passes through a shell or a configuration file that treats those characters as special, in which case quoting the whole string is the fix.
Getting the Schedule Right the First Time
A cron expression is a small language with a short grammar and a long list of edge cases. Learn the five fields in order, remember that the two day fields combine with OR, and validate every string before it reaches production. Keep a reference table of the schedules you use most, and reach for a browser-based checker when a step operator or a range makes you second-guess yourself.
Do that, and the next time you write a schedule you will know exactly when it fires rather than finding out from a failed job at the wrong hour.