Cron Expression Generator
Build a cron schedule field by field, or paste an existing expression to have it explained. You get the plain-English meaning and the next run times as you type — standard 5-field Linux crontab syntax, entirely in your browser.
Five fields: minute, hour, day of month, month, day of week. Edit below or type here.
At 03:00, on Monday through Friday
Minute
0
Minute 0
Hour
3
Hour 3
Day of month
*
Every day
Month
*
Every month
Day of week
1-5
Monday through Friday
Upcoming run times are calculated in your browser. Enable JavaScript to see them.
Cron has no time zone field: a job fires when the server's local wall clock matches the expression. These dates are a calendar reading of the schedule, counted forward from your device's current time — if your server runs a different zone, shift them accordingly.
Paste this into crontab -e. The schedule alone is not a crontab entry — the command belongs on the same line.
How to use the cron expression generator
There are two ways in, and they drive the same state. To generate an expression, work through the five field blocks: pick whether each field means every value, an interval, a set of specific values, or a range, and the expression above updates as you go. To translate an expression you already have — from an inherited crontab, a CI config, or a colleague — paste it into the expression box instead. The builder fills itself in, the plain-English line tells you what it actually means, and the next-run list shows the real dates it will fire.
Either way, check the upcoming run times before you commit the schedule — that list is the fastest way to catch an expression that parses cleanly but fires at the wrong moment. Nothing you type leaves your browser.
Cron expression format
A cron expression is five space-separated fields. Each one answers a single question about when the job runs, and they are always in this order:
* * * * *
│ │ │ │ │
│ │ │ │ └─ day of week (0–7, 0 and 7 both mean Sunday)
│ │ │ └─── month (1–12)
│ │ └───── day of month (1–31)
│ └─────── hour (0–23)
└───────── minute (0–59)The job runs when the minute, hour and month fields all match the current time and at least one of the two day fields matches. So 0 3 * * 1-5 is minute 0, hour 3, any day of the month, any month, Monday through Friday — 3:00 AM on weekdays.
An expression is not the same thing as a crontab line. A crontab entry is the schedule plus the command: 0 3 * * 1-5 /path/to/backup.sh. The generator shows both, because on its own the expression does nothing.
Cron special characters
Four characters cover everything standard cron can express:
| Character | Meaning | Example |
|---|---|---|
* | Every valid value for that field | 0 * * * * — every hour, on the hour |
, | A list of specific values | 0 9,17 * * * — at 09:00 and 17:00 |
- | An inclusive range | 0 9 * * 1-5 — Monday to Friday |
/ | A step through a range or after * | */15 * * * * — every 15 minutes |
These combine freely on Linux: 0-30/10,45 is valid, and so is 8-18/2. Two things about steps catch people out. First, a step is evaluated only inside its own field — */23 in the hour field means hour 0 and hour 23, not "every 23 hours", because there is nothing to carry the remainder into the next day. Second, a step that does not divide its field evenly restarts at the boundary, so */7 in the minute field fires at :00, :07 … :56 and then again at :00 four minutes later. Any interval that does not divide 60 — every 90 minutes, for example — needs two crontab lines rather than one clever expression.
The @ shorthands — @daily, @hourly, @weekly, @monthly, @yearly, @reboot — work on Linux, and pasting one in here expands it to the five-field equivalent. @reboot is the odd one out: it has no calendar schedule, and it fires when the cron daemon starts, which is not always a machine boot. Worth knowing that steps, the shorthands and three-letter names are all Vixie cron extensions rather than original POSIX syntax, which only has *, values, lists and ranges — relevant only if you are targeting an unusual Unix.
Common cron schedules
| Expression | When it runs |
|---|---|
* * * * * | Every minute |
*/5 * * * * | Every 5 minutes |
0 * * * * | Every hour, on the hour |
0 */6 * * * | Every 6 hours — 00:00, 06:00, 12:00, 18:00 |
0 0 * * * | Every day at midnight |
30 3 * * * | Every day at 03:30 |
0 9 * * 1-5 | Weekdays at 09:00 |
*/15 9-17 * * 1-5 | Every 15 minutes, 09:00–17:45, weekdays only |
0 0 * * 0 | Weekly, Sunday at midnight |
0 0 1 * * | Monthly, the 1st at midnight |
0 6 1 1,4,7,10 * | Quarterly — 1 January, April, July and October at 06:00 |
A scheduled script has to be executable and has to work with cron's very short PATH, so use absolute paths inside it — our chmod calculator and the guide to running a shell script cover the permissions side, and set -e can exit after some command failures, but explicit error handling is still important. For the command half of the line, rsync and disk-space checks are the two most commonly scheduled tasks.
Day of week: 0, 7 and names
Sunday can be written as either 0 or 7, because Linux cron accepts both. The original POSIX range is 0-6 with 0 as Sunday, and 7 was added by Vixie cron for the people who expected the week to end rather than start on Sunday. Three-letter names work too — 0 0 * * SUN and 0 0 * * 0 are the same schedule.
The duality has one sharp edge: 1-7 is not a tidy "Monday to Sunday" — it covers all seven days, because 7 folds back onto Sunday alongside 1 through 6. This generator accepts 7 and names on input but always writes numbers with Sunday as 0. Named ranges like mon-fri show why: cronie accepts them, Debian's cron historically did not, so numbers are the portable choice.
When day-of-month and day-of-week are both set
This is the single most misunderstood rule in cron, and it is worth reading carefully. When both day fields are restricted, cron runs the job when either one matches. They are combined with OR, not AND.
The "first Monday" trap. 0 9 1-7 * 1 looks like "9:00 on the first Monday of the month". It is not. It fires on days 1 through 7 and on every Monday — roughly ten or eleven mornings a month instead of one. The same mistake turns 0 3 22-28 * 0, an attempt at the fourth Sunday, into a job that runs all seven days of that window plus every Sunday.
The generator flags this whenever you restrict both fields, and the next-run list shows the real union, so you can see the extra dates rather than discovering them in production. To pin a single day, restrict one field and leave the other as *. Cron genuinely cannot express "the nth weekday of the month" on its own — the standard workaround is to schedule the wider window and let the command bail out early:
0 9 1-7 * * [ "$(date +%u)" = "1" ] && /path/to/script.shNote that the month field is not part of the OR rule. It always has to match, so 0 0 1,15 6 1 only ever runs in June.
Time zones and daylight saving
Cron expressions contain no time zone. A job runs when the clock on the machine running the cron daemon matches the expression, so 0 9 * * * is 09:00 UTC on a server set to UTC — which most cloud images are — and 09:00 local time on a server set to something else. The daemon runs with one configured zone and classic Debian cron has no per-user time zones; setting TZ in a crontab affects the environment the command runs in, not when it is scheduled. cronie, the cron on RHEL and Fedora, does honour a CRON_TZ= line, but that is not portable.
Daylight saving is the reason to care. On a server in a DST-observing zone the hour skipped each spring can mean a job inside it never runs that day, and the hour repeated in autumn can mean it runs twice. Behaviour varies between cron implementations, so there is no portable answer — keep servers on UTC, or avoid scheduling anything critical between 01:00 and 03:00. To confirm a job actually ran, journalctl and systemctl status are where to look.
What cron cannot express
Some schedules are simply outside the syntax, and no generator can produce them:
- Anything under a minute. There is no seconds field in Unix cron. "Every 30 seconds" needs a loop with
sleep, or a systemd timer. - "Every N days from now."
*/3in the day-of-month field restarts each month, so the gap across a month boundary is not three days. - The nth weekday of a month. Quartz has
#for this; crontab does not. - Intervals that do not divide their field — every 90 minutes, every 7 hours. These need more than one line.
If you are working with ?, L, W, #, a leading seconds field or a trailing year field, you have a Quartz or AWS EventBridge expression rather than a crontab one. They are different dialects, not extensions — pasting a six-field expression into a crontab will fail to install. For schedules that are genuinely too calendar-complex for cron, systemd timers and their OnCalendar= syntax are the usual Linux answer.
FAQ
What is a cron expression?
0 3 * * 1-5 means 3:00 AM Monday through Friday. Paired with a command in a crontab file, it becomes a scheduled task.How do I write a cron job for every 5 minutes?
*/5 * * * *. The */5 in the minute field means every fifth minute, so the job fires at :00, :05, :10 and so on, every hour of every day. Same pattern for */15 or */30 — but pick an interval that divides 60 evenly, or the gap at the top of each hour will be shorter than the rest.Why did my job run 60 times?
* left in the minute field. * 3 * * * reads as "every minute where the hour is 3" — 03:00 through 03:59, sixty executions. You wanted 0 3 * * *. The tool warns whenever the minute field is * while another field is restricted, because it is rarely what anyone intends.Can cron run a job every 30 seconds?
sleep 30 between iterations, or a systemd timer, which handles sub-minute intervals directly.Does this work with Quartz, Spring or AWS EventBridge?
?, L, W and #; AWS EventBridge adds a year field and numbers weekdays differently. Those are incompatible dialects rather than extensions, and a tool that silently accepted both would hand you schedules that run at the wrong time.