Cron Syntax for Beginners: How to Build a Schedule
Cron syntax explained in plain terms. Learn the five fields, special characters, and common schedules. Build cron expressions with our free generator tool.
What Cron Is
Cron is a time-based job scheduler built into Unix-like operating systems. It runs commands on a repeating schedule defined by a simple five-field expression. Every developer touches cron eventually: database backups, log rotation, cache clearing, email reports, and certificate renewal all depend on it.
The name comes from "chronos," the Greek word for time. The cron daemon (crond) wakes up every minute, checks all crontab files for jobs scheduled at the current minute, and runs them. It's been in Unix since Version 7 (1979) and is still the standard scheduler on Linux and macOS.
The Five Fields
A cron expression has five space-separated fields:
* * * * *
│ │ │ │ │
│ │ │ │ └── day of week (0-7, 0 and 7 = Sunday)
│ │ │ └─────── month (1-12)
│ │ └──────────── day of month (1-31)
│ └───────────────── hour (0-23)
└────────────────────── minute (0-59)
Each field accepts a number, a wildcard (*), or a combination of operators.
The fields are read left to right from the smallest unit of time (minute) to
the widest (day of week/month).
Operators
| Operator | Meaning | Example |
|---|---|---|
| * | Every value | * * * * * = every minute |
| , | List of values | 0,30 * * * * = at minutes 0 and 30 |
| - | Range of values | 1-5 * * * * = minutes 1 through 5 |
| / | Step values | */15 * * * * = every 15 minutes |
| L | Last (non-standard) | Some schedulers support L for last day |
The * wildcard means "every possible value for this field." In the minute
field, * means every minute (0 through 59). In the hour field, * means
every hour (0 through 23).
The / step operator divides the field's range. */5 in the minute field
means "every 5th minute": 0, 5, 10, 15, 20, ..., 55. It starts at the first
value of the range (0), not "every 5 minutes from now."
Common Schedules
| Expression | Meaning |
|---|---|
| * * * * * | Every minute |
| 0 * * * * | Every hour at minute 0 |
| 0 0 * * * | Daily at midnight |
| 0 0 * * 0 | Weekly at midnight Sunday |
| 0 0 1 * * | Monthly on the 1st at midnight |
| 0 0 1 1 * | Yearly on January 1st at midnight |
| */5 * * * * | Every 5 minutes |
| 0 */2 * * * | Every 2 hours |
| 30 4 * * 1-5 | 4:30 AM on weekdays |
| 0,30 * * * * | At minutes 0 and 30 |
The "Every 5 Minutes" Trap
*/5 * * * * runs at 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55. That's 12
times per hour. It does not run at 1, 2, 3, 4. This is correct for most use
cases but worth double-checking when precision matters.
If you need exactly "every 5 minutes starting now," cron can't do it directly.
The */5 always starts from 0. To offset, use a list: 3,8,13,18,23,28,...
(every 5 minutes starting at minute 3). That's tedious to type but sometimes
necessary for staggering jobs across multiple servers.
Day-of-Week and Day-of-Month Interaction
When both day-of-month and day-of-week are specified (neither is *), the job
runs when EITHER field matches. This is the "OR" rule:
0 0 1 * 1 Runs on the 1st of the month AND every Monday
0 0 * * 1 Runs only on Mondays (day-of-month is *)
0 0 1 * * Runs only on the 1st (day-of-week is *)
If you need "the 1st of the month only if it's a Monday," cron doesn't support that directly. Add the logic inside the script. Check the date at the start of the job and exit early if the condition isn't met.
How to Build and Verify a Cron Expression
- Start with what you want: "every weekday at 9 AM."
- Break it down: minute = 0, hour = 9, day-of-month = *, month = *, day-of-week = 1-5.
- Build the expression:
0 9 * * 1-5. - Verify with a Cron Generator that translates the expression back to human-readable English.
The Cron Generator works both directions: enter a
cron expression and see the human-readable description, or build a schedule
from dropdowns and get the cron expression. This is the fastest way to catch
off-by-one errors in day-of-week or to verify that */15 in the hour field
behaves as you expect.
Never trust a cron expression you wrote from memory. Always verify. A typo that
changes 0 0 * * 0 (weekly) to * 0 * * 0 (every minute for one hour once a
week) can send thousands of emails or fill a disk with log entries.
Cron in Practice
Cron expressions appear in:
- Linux crontab:
crontab -eto edit user-level cron jobs. System-wide jobs go in/etc/crontabor/etc/cron.d/. - GitHub Actions:
on: schedule: - cron: "0 0 * * *"using POSIX cron syntax (5 fields). GitHub runs on a best-effort schedule during high load. - GitLab CI:
schedulekeyword in.gitlab-ci.ymlwith the pipeline schedules feature. - Kubernetes CronJobs:
schedulefield in the CronJob spec. Supports seconds as an optional sixth field in some distributions. - Vercel cron jobs:
vercel.jsoncronsarray. Supports 5-field expressions with some limitations on minimum frequency.
Each platform uses the same five-field syntax with minor variations. GitHub
Actions runs cron jobs at the scheduled minute plus a delay of up to several
minutes during busy periods. Kubernetes CronJobs are more punctual but can
overlap if a job takes longer than its interval (use concurrencyPolicy to
control this). Always read the platform's cron documentation for quirks.
Crontab Syntax for the Terminal
To edit your crontab:
crontab -e # Edit your user crontab
crontab -l # List your current cron jobs
crontab -r # Remove your crontab (careful!)
A crontab line looks like:
# ┌─ minute (0-59)
# │ ┌─ hour (0-23)
# │ │ ┌─ day of month (1-31)
# │ │ │ ┌─ month (1-12)
# │ │ │ │ ┌─ day of week (0-7, 0/7 = Sunday)
# │ │ │ │ │
* * * * * command to run
Each user has their own crontab. Root's crontab can run commands as any user.
Try it yourself: open the Cron Generator. Build a schedule for "every Monday at 3:30 AM" using the dropdowns. The tool outputs
30 3 * * 1. Then enter*/10 9-17 * * 1-5and read the human-readable description: "Every 10 minutes, between 9:00 AM and 5:59 PM, Monday through Friday."