Every developer eventually needs to schedule something — a nightly database backup, an hourly report, a weekly cleanup job. Cron is how Unix-like systems (and countless cloud services) handle scheduled tasks, and the expression syntax has become a near-universal standard. Once you understand the five fields, the rest follows logically.
The DevToolShack Cron Expression Builder lets you build expressions visually and see a plain-English description of what the schedule means — no more guessing whether 0 9 * * 1 is 9am every Monday or every minute of the 9th hour.
The Five-Field Structure
┌─────────── minute (0–59)
│ ┌───────── hour (0–23)
│ │ ┌─────── day of month (1–31)
│ │ │ ┌───── month (1–12)
│ │ │ │ ┌─── day of week (0–7, 0 and 7 are both Sunday)
│ │ │ │ │
* * * * *
Each field accepts a specific value, a wildcard, a range, a step, or a list. The fields are always in this order — minute first, day-of-week last.
Special Characters
| Character | Meaning | Example |
|---|---|---|
* | Any / every value | * in hour = every hour |
, | List of values | 1,15 = 1st and 15th |
- | Range | 9-17 = hours 9 through 17 |
/ | Step | */15 = every 15 units |
Reading Common Expressions
| Expression | Meaning |
|---|---|
* * * * * | Every minute |
0 * * * * | Every hour (at :00) |
0 9 * * * | Every day at 9:00 AM |
0 9 * * 1 | Every Monday at 9:00 AM |
0 9 * * 1-5 | Weekdays at 9:00 AM |
0 0 1 * * | First day of every month at midnight |
0 0 1 1 * | January 1st at midnight (yearly) |
*/15 * * * * | Every 15 minutes |
0 9,17 * * * | Daily at 9:00 AM and 5:00 PM |
0 0 * * 0 | Every Sunday at midnight |
The Gotchas
Timezone is the server's timezone
Cron runs in the timezone of the system it's on — which may not be your timezone, and almost certainly isn't your users' timezone. Always check what timezone your server or cloud scheduler uses. Many cloud services (AWS EventBridge, GitHub Actions) let you specify a timezone explicitly; use this whenever you need "9am in London" rather than "9am server time".
Day-of-month AND day-of-week interact unexpectedly
When you specify both a day-of-month and a day-of-week, most cron implementations run the job when either condition is true — not both. So 0 9 1 * 1 runs at 9am on the 1st of the month and every Monday, not only on Mondays that fall on the 1st.
Month and day-of-week numbering
Day of week can be 0–7, where both 0 and 7 represent Sunday. Month is 1–12 (1 = January). These are easy to mix up — the builder handles this for you.
Six-Field Cron (with Seconds)
Some systems (Quartz scheduler, many cloud services) add a seconds field at the start:
┌──────────── second (0–59)
│ ┌────────── minute (0–59)
│ │ ┌──────── hour (0–23)
│ │ │ ┌────── day of month (1–31)
│ │ │ │ ┌──── month (1–12)
│ │ │ │ │ ┌── day of week (0–7)
│ │ │ │ │ │
* * * * * *
AWS EventBridge and some CI systems use this format. Always check the documentation for the system you're targeting — a five-field expression in a six-field system will be parsed incorrectly.
Special Strings (Where Supported)
Many cron implementations accept readable shortcuts:
@yearly = 0 0 1 1 * (once a year)
@monthly = 0 0 1 * * (once a month)
@weekly = 0 0 * * 0 (once a week, Sunday)
@daily = 0 0 * * * (once a day at midnight)
@hourly = 0 * * * * (once an hour)
@reboot (once at startup)
Using Cron in Code
// Node.js — node-cron library
import cron from 'node-cron';
// Run every day at 9:00 AM
cron.schedule('0 9 * * *', () => {
console.log('Running daily job...');
});
// Run every 15 minutes
cron.schedule('*/15 * * * *', () => {
sendHeartbeat();
});
# Python — schedule library (simpler) or APScheduler (more powerful)
from apscheduler.schedulers.blocking import BlockingScheduler
scheduler = BlockingScheduler()
@scheduler.scheduled_job('cron', hour=9, minute=0)
def daily_job():
print('Running daily job...')
scheduler.start()