Cron Scheduling Explained: Reading and Writing Cron Expressions
Cron has scheduled jobs on Unix systems for decades, and the same five-field syntax now shows up in cloud schedulers, CI pipelines, and container orchestrators. The syntax is compact to the point of looking cryptic, but it follows a small set of rules. Once those click, you can read any expression at a glance.
The five fields
A standard cron expression is five fields separated by spaces, each controlling one unit of time:
* * * * *
| | | | |
| | | | +-- day of week (0-6, Sunday is 0)
| | | +---- month (1-12)
| | +------ day of month (1-31)
| +-------- hour (0-23)
+---------- minute (0-59)A job runs when the current time matches every field. Five asterisks means every field matches always, so the job runs every minute. To run at 3:30 every day:
30 3 * * *Minute 30, hour 3, any day of month, any month, any day of week.
Steps with the slash
The slash defines a step, which means do something at a repeating interval. The most common form pairs it with an asterisk. To run every five minutes:
*/5 * * * *Read that as every fifth value starting from zero in the minute field: 0, 5, 10, 15, and so on. Every two hours is:
0 */2 * * *Note the leading zero in the minute field. Without it, every-two-hours would also fire on every minute within those hours. A step can also start from a specific point, so 10/15 in the minute field means start at 10, then every 15 after: 10, 25, 40, 55.
Ranges and lists
A hyphen makes a range. A comma makes a list of discrete values. They combine freely. To run on the hour, every hour from 9 to 17 (business hours):
0 9-17 * * *To run at 8am and 8pm only:
0 8,20 * * *To run every 15 minutes during business hours on weekdays, mix a step with ranges:
*/15 9-17 * * 1-5Many cron versions also accept names, so MON-FRI and JAN work in the day-of-week and month fields.
The day-of-month vs day-of-week trap
This one surprises experienced engineers. When both the day-of-month and day-of-week fields are restricted (neither is an asterisk), most cron implementations treat them as OR, not AND. The job runs if either condition matches.
Consider:
0 0 1 * 1You might read this as midnight on the first of the month only if it is a Monday. In practice it runs at midnight on the 1st of every month AND on every Monday. If you need an AND relationship, leave one field as an asterisk and handle the other condition inside your script. This behavior is historical and consistent across classic cron, so plan for it rather than fighting it.
Timezones
Cron has no timezone field. Jobs run in the timezone of the system or scheduler running them, which is often UTC on servers. A schedule that looks like 9am may fire at a very different local hour. Two things save you here: set the scheduler timezone explicitly where the platform allows it, and remember daylight saving transitions. On the spring-forward day a job scheduled inside the skipped hour may not run, and on fall-back it may run twice. For anything time-critical, pin to UTC and convert in your application.
Six-field Quartz cron
Not all cron is five fields. The Quartz scheduler used in the Java ecosystem, and several cloud platforms, adds a seconds field at the front and sometimes a year field at the end:
0 0 12 * * ?
|
+-- seconds (0-59)Quartz also uses the question mark to mean no specific value in the day-of-month or day-of-week field, which sidesteps the OR ambiguity above. Before copying an expression between systems, confirm whether the target expects five or six fields. A five-field expression pasted into a six-field parser shifts every value to the wrong unit.
Common expressions worth memorizing
0 * * * * every hour, on the hour
0 0 * * * every day at midnight
0 0 * * 0 every Sunday at midnight
0 0 1 * * first day of every month
*/10 * * * * every ten minutes
0 2 * * 1-5 2am on weekdaysWhen you need something less routine, building the expression by picking values beats guessing and watching logs the next morning.
Related tools: