A cron job is a command run on a schedule by a daemon that checks every minute for work that is due. The schedule and the command sit on one line in a file called a crontab, written as five fields: minute, hour, day of month, month and weekday.
- Five fields: minute, hour, day, month, weekday
- Edit your own with crontab -e
- One minute is the smallest interval
- Output goes to mail, and is discarded without it
- Restrict both day fields and it matches either
On this page
SyntaxThe five fields
Every cron line is a schedule followed by a command. The schedule is five fields separated by spaces, and once the order is memorized the rest is detail.
* * * * * /usr/local/bin/backup.sh
| | | | |
| | | | +-- weekday, 0 to 6, Sunday is 0
| | | +---- month, 1 to 12
| | +------ day of month, 1 to 31
| +-------- hour, 0 to 23
+---------- minute, 0 to 59
An asterisk means every value. Beyond that there are four operators, and between them they express almost anything.
| Operator | Written | Meaning |
|---|---|---|
| Every | * | Every value of that field |
| List | 1,15,30 | Exactly those values |
| Range | 9-17 | Every value from 9 to 17 |
| Step | */15 | Every fifteenth value, so 0, 15, 30, 45 |
| Combined | 0-30/10 | Every tenth value in that range |
Read as a sentence, a line says: run this command at these minutes, of these hours, on these days.
Lines worth recognizing
0 3 * * * every day at 03:00
*/15 * * * * every fifteen minutes
0 9-17 * * 1-5 on the hour, nine to five, Monday to Friday
0 0 1 * * at midnight on the first of the month
30 2 * * 0 02:30 every Sunday
@reboot once, when the machine starts
The named shortcuts, @daily, @weekly, @monthly and @reboot, are easier to read and worth using where they fit. @daily means midnight, which is exactly when everyone else's daily job also runs.
The day of month and day of week trap
This is the one piece of cron syntax that behaves unlike the rest. If both the day of month and the day of week fields are restricted, cron runs the job when either matches, not both.
0 0 13 * 5 midnight on the 13th, AND every Friday
That line does not mean Friday the 13th. It means every 13th and every Friday. To get the combination you need a check inside the command itself. This surprises people who have used cron for years, because every other pair of fields is an AND.
The filesWhere crontabs live
There is not one crontab. There are several, and knowing which one you are looking at saves an afternoon.
A user crontab belongs to one account and runs as that account. Edit it with crontab -e, list it with crontab -l. It has five schedule fields and then the command. Never edit the underlying file directly, because the command that edits it also validates it and tells the daemon to reload.
The system crontab, /etc/crontab, has an extra field between the schedule and the command: the user to run as. A line copied from a user crontab into this file will fail, because the first word of the command is read as a username.
Drop in directories, /etc/cron.d/, take files in the system format. This is where packages put their jobs, and it is the right place for anything configuration management deploys.
The interval directories, /etc/cron.hourly/, daily, weekly and monthly, take executable scripts with no schedule at all. Drop a script in, make it executable, and it runs at that interval. Note that a file with a dot in its name is often ignored, which is why backup.sh in one of these directories can sit there doing nothing.
In practiceWhat people actually schedule
The same handful of tasks account for most cron jobs on most servers, and each one has a failure mode worth knowing before you write it.
Backups. The most common scheduled job and the one that fails most quietly, because nobody looks at a backup until they need it. Schedule it off peak, log the result, and test a restore on a schedule of its own.
Log rotation and cleanup. Compressing and deleting old files so a disk does not fill. On most systems logrotate already runs from cron daily, and the job people add is for application logs it does not know about.
Certificate renewal. Certificates from the free authorities last ninety days, so the renewal task runs twice a day and does nothing until it is needed. A renewal that runs but never reloads the web server is the classic version of this failing.
Database maintenance. Reindexing, vacuuming, dumping. Heavy work that belongs at a quiet hour, and the reason overlap protection matters, because a maintenance job that has not finished when the next one starts will fight itself.
Reports and exports. Generating a file or an email on a schedule. Usually harmless when it fails, which is why it fails for months.
Cache and temporary file cleanup. Deleting files older than some age. The dangerous one, because a path variable that ends up empty turns a cleanup command into a command that deletes from the root of the filesystem.
Polling something. Checking an inbox, a queue or an API every few minutes. Frequently the wrong tool, since a scheduled task that polls every minute is a service that would rather be running continuously. Where the other side offers one, a webhook removes the schedule altogether by having the event announce itself rather than waiting to be asked about.
Two rules cover almost all of these. Anything that writes needs to be safe to run twice, and anything that matters needs to say so when it finishes.
DebuggingWhy your cron job did not run
Nearly every cron job problem is one of five things, and none of them is cron itself being broken.
The environment is not your environment. Cron runs with a minimal set of variables and a short PATH. A command that works in your shell fails here because the shell that started it read no profile, no bashrc and no environment file.
The fix is to use absolute paths for every command, or to set PATH at the top of the crontab.
The working directory is not where you think. Jobs start in the home directory of the user, not in the directory the script lives in. Relative paths inside the script resolve somewhere unexpected.
The output went nowhere. Cron mails output to the user, and on a machine with no mail configured that mail is discarded. A job that fails every night can do so in complete silence for months. Redirect output to a file and the problem becomes visible immediately.
The percent sign. Inside a crontab, an unescaped % is turned into a newline and everything after it becomes input to the command. This bites hardest with date formats, where date +%Y-%m-%d truncates at the first percent unless each one is escaped with a backslash.
Permissions and ownership. The job runs as the crontab's owner, which is often not the user who wrote it and almost never root unless it was placed in a root crontab deliberately. A script that reads a file the owner cannot read simply fails.
The diagnostic order that works: check the daemon is running, look for the job in the system log, then run the command with a deliberately emptied environment and see whether it still works.
grep CRON /var/log/syslog | tail -20
env -i /bin/sh -c '/usr/local/bin/backup.sh'
The second line is the one that finds the environment problems, because it runs the script the way cron will rather than the way your shell would.
ReliabilityMaking a job you can trust
A scheduled task that nobody watches is a scheduled task that stops working eventually, and the first sign is usually a restore that fails.
Capture the output. Redirect both streams to a log file with a timestamp. >> /var/log/backup.log 2>&1 is the whole change.
Stop it overlapping itself. A job that takes eleven minutes on a ten minute schedule will run two copies, then three. flock solves this in one word and is present on every modern system.
*/10 * * * * /usr/bin/flock -n /tmp/sync.lock /usr/local/bin/sync.sh
Make it tell you it succeeded. Silence is not success, and cron cannot tell the difference. Monitoring is covered on its own below, because it is the difference between a job you have and a job you can rely on.
Make it safe to run twice. Machines reboot, jobs get triggered by hand, schedules overlap at the change of clocks. A job that is harmless when run twice is a job you never have to think about.
Watch the clock changes. With daylight saving, a job scheduled inside the hour that disappears in spring does not run, and one inside the hour that repeats in autumn runs twice. Anything financial belongs at a time of day that exists all year, or on a machine set to UTC.
MonitoringMonitoring, because silence is not success
Cron has no idea whether a command worked. It starts the process, and its interest ends there. The log line saying it ran says nothing about the exit code, and a job that has failed every night since a path changed looks exactly like a job that succeeded.
The pattern that solves this is a dead man's switch. The job reports in when it finishes successfully, and a monitor raises an alarm when the report does not arrive.
It is the inversion that matters: you are alerting on the absence of success rather than the presence of failure, which is the only way to catch a job that stopped running at all.
0 3 * * * /usr/local/bin/backup.sh && curl -fsS https://monitor.example/ping/abc123
The two ampersands are doing the work. The ping only happens if the script exited zero, so a failing backup stops reporting, and the monitor notices the silence. Several hosted services exist for the receiving end, and a small internal one is not hard to write.
Three things are worth monitoring per job: that it ran, that it exited zero, and how long it took. The third catches the job that is still succeeding but has grown from four minutes to fifty and is about to start overlapping itself.
On WindowsWindows, and the equivalent
Windows has no cron. The equivalent is Task Scheduler, which does the same job with a different model: tasks are objects with triggers, actions and conditions rather than lines in a file, and they can be created from the command line with schtasks or from PowerShell with the scheduled task commands.
Two differences matter in practice. A Windows task records its last result and its next run time, which cron does not. And a Windows task can be set to run whether or not a user is logged on, which requires storing credentials, and which is the source of most tasks that stop working after a password change.
ComparisonCron and a systemd timer, where they actually differ
| Criterion | Cron | systemd timer |
|---|---|---|
| Where the schedule lives | One line in a crontab | Two files, a timer and a service |
| Learning cost | Five fields and you are done | A unit syntax to learn |
| Logging | Mail, or wherever you redirect it | In the journal, per unit, automatically |
| Missed while powered off | Never runs | Can run on the next boot |
| Preventing overlap | Needs flock | Built in |
| Resource limits | None | Memory and CPU caps per job |
| Dependencies on other services | None | Can wait for the network or a mount |
| Portable to any Unix | Yes | No, systemd only |
Most Linux distributions now ship systemd, which brings its own scheduler. Cron still works everywhere and is not going away, but for anything new the comparison is worth making.
The honest summary: cron for something small on a machine you may not control, a timer for anything that matters on a modern Linux server. The logging alone is worth the extra file, because a timer's output is in the journal whether or not anyone configured mail.
FAQFrequently asked questions
What is a cron job in simple terms?
A command the system runs for you on a schedule, written as one line containing when to run it and what to run.
What do the five stars mean?
Minute, hour, day of month, month, and day of week. An asterisk means every value of that field, so five asterisks means every minute of every day.
How do I edit my cron jobs?
crontab -e opens your own crontab in an editor and validates it when you save. crontab -l lists it. Editing the file under /var/spool directly skips the validation and the reload.
Why does my cron job work manually but not on schedule?
Almost always the environment. Cron runs with a minimal PATH and no profile, so use absolute paths for every command and set PATH at the top of the crontab.
Can cron run something more often than once a minute?
No. One minute is the smallest interval. For anything faster, run a small loop as a service instead of scheduling it.
Where does the output go?
To mail, addressed to the crontab owner. On a machine without mail configured it is discarded, which is why failing jobs are silent. Redirect to a file.
What does @reboot do?
Runs the command once when cron starts after a boot. Useful for starting something at boot without writing a service, and less reliable than a real service unit.
Why did my job run on both the 13th and every Friday?
Because when both day fields are restricted, cron treats them as either rather than both. It is the one field pair that behaves this way.
How do I stop two copies running at once?
Wrap the command in flock -n with a lock file. If the previous run is still going, the new one exits instead of piling up.
How do I check whether a job actually ran?
Look for CRON entries in the system log or the journal. The log records that cron started the command, which is not the same as the command succeeding, so a job that matters needs its own logging too.
Should I use cron or a systemd timer?
A timer for anything important on a modern Linux server, mostly for the logging and the built in overlap protection. Cron for something small, or on a system where systemd is not available.
What is the Windows equivalent?
Task Scheduler. Same idea, different model, and it records the result of the last run, which cron does not.
Keep readingRelated concepts
Read next · Containers What Is Docker? Scheduling inside a container is its own problem, because the container may not be running. Open this next14 min- Operations · 14 min What a Webhook Is, and the Four Things That Break One The polling this exists to remove.
- Operations · 10 min The ELK Stack, and the Licensing Question Underneath It Where the output of those jobs ends up when somebody decides to keep it.