CRON expressions are five-field strings that tell a scheduler exactly when to run a task. They look cryptic at first β */5 * * * * β but the logic is completely regular once you know what each field means. This guide walks through all five fields, every special character, and thirty real-world examples you can copy and test immediately.
Want to build or test a CRON expression visually?
Open CRON Expression Generator βThe five fields
A standard CRON expression has exactly five fields, separated by spaces:
ββββββββββββββ minute (0β59)
β ββββββββββββ hour (0β23)
β β ββββββββββ day of month (1β31)
β β β ββββββββ month (1β12)
β β β β ββββββ day of week (0β6, 0 = Sunday)
β β β β β
* * * * *
So 30 8 * * 1 means: at minute 30, hour 8, any day of month, any month, only on Monday β which is "every Monday at 8:30 AM".
Special characters
* β every value
An asterisk matches every possible value for that field.
* * * * * β runs every minute
*/n β every n-th value (step)
The slash means "step". */5 means every 5th value starting from the minimum. In the minute field that is 0, 5, 10, 15, β¦, 55.
*/5 * * * * β every 5 minutes
* */2 * * * β every 2 hours (00:00, 02:00, 04:00, β¦)
*/30 * * * * β every 30 minutes
n-m β range
A hyphen defines a continuous range of values, inclusive.
0 9 * * 1-5 β weekdays (MonβFri) at 9:00 AM
0 8-18 * * * β every hour from 8 AM to 6 PM
n,m,p β list
Comma-separated values specify multiple discrete values. No spaces around the commas.
0 9,17 * * * β 9:00 AM and 5:00 PM every day
0 0 1,15 * * β midnight on the 1st and 15th of each month
n-m/p β range with step
Combines a range and a step: matches every p-th value within the range n through m.
0-30/10 * * * * β minutes 0, 10, 20, 30 of every hour
0 8-18/2 * * * β every 2 hours between 8 AM and 6 PM
Common schedules β ready to copy
| Expression | Meaning |
|---|---|
* * * * * | Every minute |
*/5 * * * * | Every 5 minutes |
*/15 * * * * | Every 15 minutes |
0 * * * * | At the start of every hour |
0 0 * * * | Every day at midnight |
0 6 * * * | Every day at 6:00 AM |
0 9 * * 1-5 | Weekdays at 9:00 AM |
0 9,17 * * 1-5 | Weekdays at 9 AM and 5 PM |
0 0 * * 0 | Every Sunday at midnight |
0 0 * * 1 | Every Monday at midnight |
0 0 1 * * | First day of every month at midnight |
0 0 1,15 * * | 1st and 15th of every month at midnight |
0 0 L * * | Last day of the month (Linux cron / Quartz only) |
0 0 1 1 * | January 1st at midnight (yearly) |
0 */4 * * * | Every 4 hours (midnight, 4 AM, 8 AM, β¦) |
30 23 * * * | Every day at 11:30 PM |
0 8-18 * * 1-5 | Business hours (8 AMβ6 PM, MonβFri) |
0 0 * * 6,0 | Every weekend (Sat and Sun) at midnight |
Day-of-month vs day-of-week: the OR rule
One of the most confusing behaviours in CRON: if both the day-of-month and day-of-week fields are set to something other than *, most systems use OR logic, not AND.
For example, 0 0 15 * 1 runs at midnight on the 15th of every month and also on every Monday. If you wanted "the 15th only if it is a Monday", you cannot express that in a standard CRON expression β you would need to handle it inside the script itself.
If one of the two fields is *, the OR rule collapses to the non-wildcard field only. So 0 0 15 * * runs only on the 15th, and 0 0 * * 1 runs only on Mondays.
How CRON handles time zones
Classic crontab on Linux always uses the server's local time zone (set by /etc/timezone or the TZ environment variable). Cloud schedulers like AWS EventBridge, GitHub Actions scheduled workflows, and Google Cloud Scheduler default to UTC.
The safe default: always write CRON expressions in UTC and document the UTC offset alongside the job definition. This avoids surprises when daylight saving time shifts clocks by an hour β a job set to run "at 2:00 AM" will either skip or run twice on DST changeover days if the schedule is in local time.
CRON in cloud and CI environments
CRON syntax is ubiquitous in modern infrastructure:
- GitHub Actions β
on: schedule: cron: "0 9 * * 1"triggers a workflow every Monday at 9:00 UTC. - AWS EventBridge (CloudWatch Events) β uses the same five-field syntax but adds a 6th field for seconds in some places.
- Kubernetes CronJob β the
spec.schedulefield accepts a standard five-field expression. - Google Cloud Scheduler β accepts standard CRON expressions, runs in UTC by default (configurable per job).
- Heroku Scheduler β limited syntax; supports only
every 10 minutes,every hour, andevery day at HH:00.
Why expressions are harder to read than they look
A few traps that catch even experienced engineers:
- Month and weekday are 1-based and 0-based respectively. Month
1is January; weekday0is Sunday (not Monday). Some implementations also accept7as Sunday β check your platform's docs. - Minutes come first, not hours. It is easy to swap them.
0 8 * * *is 8:00 AM;8 0 * * *is 0:08 AM. - Ranges are inclusive on both ends.
1-5in the weekday field means Monday through Friday, and includes both 1 and 5. */nstarts from zero (or the field minimum).*/7in the day-of-month field matches days 1, 8, 15, 22, 29 β not "every 7 days from today".
Testing before you deploy
Getting a CRON expression wrong is easy. Getting the consequences wrong β a billing job that runs every minute instead of every month β is expensive. Always test before deploying:
- Use the CRON Expression Generator on this site to see the plain-English description and the next 5 scheduled datetimes.
- On Linux, use
cronitor selector installcron-descriptor(pip install cron-descriptor) to convert an expression to English at the command line. - In Node.js,
cron-parser(npm install cron-parser) lets you iterate over the next N fire times programmatically. - For Kubernetes CronJobs, run a manual job first (
kubectl create job --from=cronjob/<name>) to validate the job script itself before the schedule triggers it.
Quick reference: all special characters
| Character | Name | Example | Meaning |
|---|---|---|---|
* | Wildcard | * | Every value |
*/n | Step | */5 | Every 5th value (0, 5, 10, β¦) |
- | Range | 1-5 | Values 1 through 5 inclusive |
, | List | 1,15 | Values 1 and 15 |
n-m/p | Range + step | 8-18/2 | Every 2nd value from 8 to 18 |
Summary
CRON expressions follow a consistent five-field pattern: minute hour day-of-month month day-of-week. Master the four special characters (*, /, -, ,) and you can express almost any recurring schedule. The main pitfalls are field order confusion, the month/weekday OR rule, and time zone assumptions β all of which you can catch before deploying by running the expression through a tester and reading the plain-English description.
Ready to build or test your CRON expression?
Open CRON Expression Generator β