What Is a Cron Job? Cron Syntax, Examples, and How to Write One
A cron job is a command your server runs automatically on a schedule you define. Back up a database every night, clear a cache every 15 minutes, email a report every Monday at 9am — that is cron. It has been the standard scheduler on Unix and Linux since the 1970s, and it is still what runs most of the internet's background work.
The whole thing is controlled by five numbers separated by spaces — that is the entire syntax. It is also why cron catches people out: five asterisks look simple until one wrong field turns a monthly job into an every-minute one.
This guide explains each field, gives you copy-paste expressions for the schedules people actually need, and covers the rule that silently breaks more cron jobs than any other — the day-of-month / day-of-week trap. If you would rather not hand-write it, our free Cron Expression Generator builds a valid expression from dropdowns in your browser.
What Is a Cron Job, Exactly?
Three words get used interchangeably but are not the same:
- cron — the daemon that wakes every minute, checks what is scheduled, and runs it. The name traces to chronos, Greek for time.
- crontab — the "cron table" file listing your jobs. Each user gets their own.
- cron job — one line in that file: a schedule plus a command.
A cron job line has six parts — five schedule fields and then the command to run:
* * * * * /path/to/command
│ │ │ │ │
│ │ │ │ └─ day of week (0-7, Sunday is 0 or 7)
│ │ │ └──── month (1-12)
│ │ └─────── day of month (1-31)
│ └────────── hour (0-23)
└───────────── minute (0-59)
An asterisk means "every", so * * * * * runs every minute of every day — the most common accidental setting there is.
The Five Cron Fields and Their Allowed Values
These ranges come straight from the crontab manual page (man 5 crontab):
| Position | Field | Allowed values | Notes |
|---|---|---|---|
| 1st | Minute | 0-59 | Minute past the hour |
| 2nd | Hour | 0-23 | 24-hour clock. Midnight is 0, not 24 |
| 3rd | Day of month | 1-31 | Starts at 1, not 0 |
| 4th | Month | 1-12 | Or names: JAN-DEC |
| 5th | Day of week | 0-7 | 0 and 7 both mean Sunday. Or names: SUN-SAT |
Two of these catch people out constantly: day of month starts at 1 while every other numeric field starts at 0, and day of week has eight valid values for seven days. Names work for months and weekdays (MON, FEB, case-insensitive), but ranges and lists of names are not portable — write 1-5, not MON-FRI.
The Five Special Characters
Every cron expression you will ever write is built from these:
| Character | Name | Meaning | Example |
|---|---|---|---|
* | Asterisk | Every possible value | * * * * * = every minute |
, | Comma | A list of specific values | 0 9,17 * * * = 9am and 5pm |
- | Hyphen | An inclusive range | 0 9-17 * * * = hourly, 9am to 5pm |
/ | Slash | Step values — "every Nth" | */10 * * * * = every 10 minutes |
| names | Three-letter | Month or weekday abbreviations | 0 0 1 JAN * = 1 January |
Steps can follow a range as well as an asterisk. 0-23/2 in the hour field means every second hour, which is the same as */2. And you can combine them freely: 0 8-18/2 * * 1-5 runs every two hours between 8am and 6pm, Monday to Friday.
One warning on steps: they divide the field's range rather than creating a rolling interval, so */40 is not "every 40 minutes" — see the FAQ below.
How to Write a Cron Expression in Five Steps
- Start with five asterisks:
* * * * *. This runs every minute — you will now restrict it. - Set the minute:
0for on-the-hour, or a step like*/15for an interval. Leave*only if you want 60 runs an hour. - Set the hour on a 24-hour clock — 9am is
9, 5pm is17, midnight is0. - Set the day. Pick either day of month or day of week; leave the other as
*. Setting both does not mean what you think — see the next section. - Set the month (
*unless the job is seasonal), then append the command with an absolute path.
Or skip the arithmetic: open the Cron Expression Generator, choose each field from a dropdown, and copy the finished expression. It runs entirely in your browser — nothing is sent to a server.
Cron Examples You Can Copy
The schedules people actually search for, ready to paste:
| Expression | When it runs |
|---|---|
* * * * * | Every minute |
*/5 * * * * | Every 5 minutes |
*/10 * * * * | Every 10 minutes |
*/15 * * * * | Every 15 minutes |
0,30 * * * * | Every 30 minutes (on the hour and half past) |
0 * * * * | Every hour, on the hour |
0 */2 * * * | Every 2 hours |
0 0 * * * | Every day at midnight |
30 2 * * * | Every day at 2:30am |
0 9 * * 1-5 | 9am, Monday to Friday |
0 9,17 * * 1-5 | 9am and 5pm on weekdays |
0 0 * * 0 | Every Sunday at midnight |
0 9 * * 6,0 | 9am on weekends |
0 0 1 * * | First day of every month, midnight |
0 0 1 1 * | Once a year, 1 January |
0 8-18 * * 1-5 | Hourly during business hours, weekdays |
Standard cron has no expression for "the last day of the month". Run it daily and let the script exit early unless tomorrow is the 1st.
The Shortcuts: @daily, @reboot and Friends
Instead of five fields you can use one of eight special strings. These are documented in the crontab manual and supported by most modern cron implementations:
| Shortcut | Equivalent | Meaning |
|---|---|---|
@reboot | — | Run once, at startup |
@yearly / @annually | 0 0 1 1 * | Once a year |
@monthly | 0 0 1 * * | Once a month |
@weekly | 0 0 * * 0 | Once a week |
@daily / @midnight | 0 0 * * * | Once a day |
@hourly | 0 * * * * | Once an hour |
@reboot is the useful one — it has no five-field equivalent and is how you start a process at boot. It fires when cron starts, which is early in the boot sequence, so if your job needs the network or a database, add a sleep 30 or use a systemd unit instead.
The Day-of-Month / Day-of-Week Trap
This is the single most misunderstood rule in cron, and it will silently run your job far more often than you intended.
The two day fields are joined with OR, not AND. The crontab manual states it plainly: "If both fields are restricted (ie, are not *), the command will be run when either field matches the current time."
So this expression:
0 0 13 * 5
does not mean "midnight on Friday the 13th". It means midnight on the 13th of every month, plus midnight every Friday — roughly 64 runs a year instead of the one or two you wanted. The manual's own example makes the same point: 30 4 1,15 * 5 runs at 4:30am on the 1st and 15th of each month plus every Friday.
The rule in practice:
- Restricting one day field and leaving the other as
*behaves exactly as you expect. - Restricting both gives you the union of the two, not the intersection.
- There is no cron syntax for the intersection. If you need "Friday the 13th", schedule
0 0 13 * *and have the script check the weekday itself before doing any work.
Dropdown-based builders make this easy to produce by accident. If your expression has a number in both day columns, set one back to *.
Installing a Cron Job: crontab -e, -l and -r
Writing the expression is half the job. Getting it into the crontab is the other half:
- Open your crontab for editing:
crontab -e. The first run asks which editor you want; nano is the safe choice. - Add your job on its own line, schedule first, then the command with absolute paths:
0 3 * * * /usr/bin/php /var/www/html/backup.php - Save and exit. In nano that is Ctrl+O, Enter, then Ctrl+X. Cron confirms with
crontab: installing new crontab. - Verify it landed:
crontab -llists every job for the current user.
Three more commands worth knowing:
crontab -l > backup.txt— save a copy before changing anything.crontab -r— deletes your entire crontab with no confirmation and no undo. It sits one key from-e. Back up first.sudo crontab -e -u username— edit another user's crontab.
End every line with a newline. Some cron implementations silently ignore a job on the last line without one.
Why Your Cron Job Is Not Running
The expression is usually fine. It is almost always the environment. In rough order of how often each one is the culprit:
- Relative paths. Cron does not start in your home directory and does not run your shell profile. Use absolute paths for the interpreter, the script, and every file the script touches:
/usr/bin/python3 /home/me/job.py, notpython3 job.py. - A minimal PATH. Cron's PATH is typically just
/usr/bin:/bin. Anything installed elsewhere — node via nvm, a Homebrew binary, a virtualenv — will not be found. Either use full paths or setPATH=at the top of the crontab. - The script is not executable. Run
chmod +x /path/to/script.sh, and make sure the file starts with a shebang such as#!/bin/bash. - Unescaped percent signs. In a crontab
%becomes a newline, and everything after the first one is fed to the command as stdin. This breaksdateformats constantly — writedate +\%Y-\%m-\%d, escaping each one. - Output going nowhere. Cron emails output to the user, which on most servers is a black hole. Redirect instead:
>> /var/log/myjob.log 2>&1captures errors too, turning an invisible failure into a readable one. - The wrong timezone. Cron uses the system timezone, usually UTC on cloud servers. Check with
timedatectl. Our Timestamp Converter and Epoch Converter translate between UTC and local time — the Unix timestamp guide covers the conversions in depth. - Cron is not running at all. Confirm with
systemctl status cron(Debian/Ubuntu) orsystemctl status crond(RHEL/CentOS).
To debug fast: set the job to * * * * * with logging on, wait two minutes, read the log. That tells you whether the schedule or the command is at fault — two very different fixes.
Cron Outside Plain Linux
The same five-field syntax turns up in a lot of places, with small but important differences:
- cPanel / shared hosting (PHP). Most hosts expose a "Cron Jobs" page taking the same five fields. Point it at the PHP binary and an absolute path:
/usr/local/bin/php /home/user/public_html/cron.php. Many enforce a 5- or 15-minute minimum interval. - systemd timers. The modern Linux replacement — more verbose, but they log to journalctl, handle dependencies, and can catch up on missed runs with
Persistent=true, which cron cannot do. - Windows. No cron. Use Task Scheduler or
schtasks. WSL includes cron but does not start it automatically. - AWS, Kubernetes and CI. EventBridge, Kubernetes CronJobs and GitHub Actions all accept cron expressions — but watch the dialects. EventBridge uses six fields and requires
?instead of*in one day field, which neatly sidesteps the OR trap above. - macOS. Cron works but is deprecated in favour of launchd.
Tips for Cron Jobs That Do Not Break
- Always redirect output to a log.
>> /var/log/job.log 2>&1on every job. A cron job that has failed silently for six months is worse than no cron job. - Back up before editing.
crontab -l > ~/crontab-backup.txtcosts a second and survives a mistypedcrontab -r. - Stagger your schedules. Eight jobs all firing at
0 0 * * *compete for the same disk. Spread them: 0:00, 0:07, 0:15. - Guard against overlap. A 5-minute job that sometimes takes 7 will run against itself. Wrap it:
flock -n /tmp/job.lock /path/to/script. - Comment every job and test the command manually first — absolute paths and all — before scheduling it.
- Avoid 1am-3am. Jobs in that window can be skipped or run twice when daylight saving shifts. Run the server in UTC if the timing matters.
Frequently Asked Questions
What does cron stand for? ▼
What is the difference between cron and crontab? ▼
crontab -e). Cron reads crontabs; crontabs do nothing on their own.How do I run a cron job every 5 minutes? ▼
*/5 * * * *. That fires at minutes 0, 5, 10 and so on past every hour. The same pattern gives you any even interval — */10, */15, */30. Build it visually with the Cron Expression Generator.Can a cron job run more often than once a minute? ▼
sleep, or use a proper daemon or job queue instead.Why does my cron job work in the terminal but not from cron? ▼
>> /tmp/job.log 2>&1 to see the real error.Does 0 0 13 * 5 mean Friday the 13th? ▼
What timezone do cron jobs use? ▼
date or timedatectl. Some cron implementations let you set CRON_TZ= at the top of the crontab. Use the Timestamp Converter to work out what your schedule means in local time.How do I delete a cron job? ▼
crontab -e, delete or comment out the line with #, then save. Avoid crontab -r — it wipes every job for the user with no confirmation and no undo.What happens if the server is off when a job was scheduled? ▼
anacron (designed for machines that are not always on) or a systemd timer with Persistent=true.Is */40 the same as every 40 minutes? ▼
*/40 fires at minute 0 and minute 40, then waits 20 minutes for the next hour. Only steps that divide evenly into 60 give even spacing.Ready to try it?
Use the tool right now — free, no signup, no upload.