Linux cron and crontab
last modified October 23, 2026
cron is the classic Linux job scheduler. The cron daemon runs in
the background and executes commands at specified times: every minute, every
night at 2:30, on weekdays during working hours, and so on. Each user has a
personal table of jobs, the crontab, which is managed with the
crontab command.
Cron is used for backups, log rotation, report generation, cleanup tasks, and other recurring maintenance. The examples use the cron implementation from Debian; other implementations such as cronie behave the same for the features shown here.
Crontab syntax
Each job is one line with five time fields followed by the command.
# ┌───────── minute (0-59) # │ ┌─────── hour (0-23) # │ │ ┌───── day of month (1-31) # │ │ │ ┌─── month (1-12 or jan-dec) # │ │ │ │ ┌─ day of week (0-7 or sun-sat; 0 and 7 are Sunday) # │ │ │ │ │ * * * * * command
A field can contain a single value, * for every value, a list
such as 1,15, a range such as 9-17, or a step such
as */15. If both the day of month and the day of week are
restricted, the job runs when either of them matches. Lines starting with
# are comments.
| Schedule | Meaning |
|---|---|
* * * * * | Every minute |
*/5 * * * * | Every five minutes |
30 2 * * * | Every day at 2:30 |
0 9 * * 1-5 | At 9:00 on weekdays |
*/15 9-17 * * 1-5 | Every 15 minutes from 9:00 to 17:45 on weekdays |
0 0 1 * * | At midnight on the first day of every month |
0 4 * * sun | At 4:00 every Sunday |
Listing the crontab
The -l option prints the current user's crontab.
crontab -l
A new user has no crontab yet.
$ crontab -l no crontab for jano
Installing jobs
The usual way to edit the crontab is crontab -e, which opens it
in the editor set in the EDITOR or VISUAL variable
and installs it after saving. A crontab can also be installed from a file,
which is convenient for keeping jobs in version control.
# m h dom mon dow command * * * * * date >> $HOME/cron-test.log * * * * * env > $HOME/cron-env.txt * * * * * echo "today is $(date +\%F)" >> $HOME/dates.log * * * * * $HOME/work/bin/backup.sh >> $HOME/backup.log 2>&1 30 2 * * * $HOME/work/bin/backup.sh */15 9-17 * * 1-5 $HOME/work/bin/check.sh
The first four jobs run every minute so that we can see their effect: they log the date, save the environment of a cron job, use the date in a command, and run a backup script. The last two are typical production schedules. Note that installing a file replaces the whole crontab; it does not add to it.
$ crontab jobs.cron $ crontab -l # m h dom mon dow command * * * * * date >> $HOME/cron-test.log * * * * * env > $HOME/cron-env.txt * * * * * echo "today is $(date +\%F)" >> $HOME/dates.log * * * * * $HOME/work/bin/backup.sh >> $HOME/backup.log 2>&1 30 2 * * * $HOME/work/bin/backup.sh */15 9-17 * * 1-5 $HOME/work/bin/check.sh
The cron environment
After the next full minute, cron runs the jobs. A job runs with a very minimal environment, not with the environment of our login shell.
cat ~/cron-test.log cat ~/cron-env.txt
The job ran at the start of the minute. Its PATH contains only
/usr/bin and /bin, and commands are executed by
/bin/sh, not Bash. Variables from ~/.bashrc are not
available. This is the most common reason why a command works in the
terminal but fails in cron. Use full paths, or set variables at the top of
the crontab, for example PATH=/usr/local/bin:/usr/bin:/bin or
SHELL=/bin/bash.
$ cat ~/cron-test.log Fri Oct 23 11:51:01 CEST 2026 $ cat ~/cron-env.txt HOME=/home/jano LOGNAME=jano PATH=/usr/bin:/bin SHELL=/bin/sh PWD=/home/jano
Percent signs
In a crontab, the % character has a special meaning: it ends
the command and starts standard input. It must be escaped as
\%.
* * * * * echo "today is $(date +\%F)" >> $HOME/dates.log
Thanks to the backslash, date receives the %F format
and the job works. Without it, the command would be cut off at the percent
sign. Moving complex commands into a script avoids the issue.
$ cat ~/dates.log today is 2026-10-23
Logging output and errors
By default, cron mails the output of a job to the user, which often goes unnoticed or fails when no mail system is installed. It is better to redirect the output to a log file.
#!/bin/bash echo "backup started at $(date +%T)" tar -czf /tmp/docs.tar.gz "$HOME/docs" echo "backup finished"
The job line $HOME/work/bin/backup.sh >> $HOME/backup.log 2>&1
appends both standard output and standard error to a log. The log reveals
that the backup failed because the docs directory does not
exist, yet the script still reported success. Adding set -e to
the script would stop it at the first error. A script must be executable
(chmod +x) to be run directly by cron.
$ cat ~/backup.log backup started at 11:51:01 tar: Removing leading `/' from member names tar: /home/jano/docs: Cannot stat: No such file or directory tar: Exiting with failure status due to previous errors backup finished
Special schedule strings
Most cron implementations support shortcuts for common schedules.
@reboot $HOME/bin/start-agent.sh @hourly $HOME/bin/sync.sh @daily $HOME/bin/backup.sh @weekly $HOME/bin/cleanup.sh @monthly $HOME/bin/report.sh
@daily is equivalent to 0 0 * * *, and the other
strings work similarly. @reboot runs the job once when the cron
daemon starts after boot.
System cron jobs
Besides user crontabs, there are system-wide jobs in
/etc/crontab and /etc/cron.d/, and directories of
scripts run at fixed intervals.
grep -v "^#" /etc/crontab ls /etc/cron.daily run-parts --test /etc/cron.daily
System crontab lines have an extra field with the user name the job runs as.
On Debian and Ubuntu, /etc/crontab runs the scripts in
/etc/cron.hourly, cron.daily,
cron.weekly, and cron.monthly with
run-parts; when anacron is installed, it takes over the daily,
weekly, and monthly runs so they are not skipped while the computer is off.
The --test option lists the scripts that would be run. Note that
run-parts ignores file names containing dots, so a script named
backup.sh in these directories is silently skipped.
$ grep -v "^#" /etc/crontab
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
17 * * * * root cd / && run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || { cd / && run-parts --report /etc/cron.daily; }
47 6 * * 7 root test -x /usr/sbin/anacron || { cd / && run-parts --report /etc/cron.weekly; }
52 6 1 * * root test -x /usr/sbin/anacron || { cd / && run-parts --report /etc/cron.monthly; }
$ ls /etc/cron.daily
apt-compat
dpkg
$ run-parts --test /etc/cron.daily
/etc/cron.daily/apt-compat
/etc/cron.daily/dpkg
Preventing overlapping runs
If a job takes longer than its interval, cron starts another instance
anyway. The flock command prevents this with a lock file.
*/5 * * * * flock -n /tmp/backup.lock $HOME/bin/backup.sh
To demonstrate, we hold the lock with a long-running command and try to
acquire it again. With -n, flock does not wait for
the lock; it gives up immediately with exit status 1, so the second
instance does not run.
$ flock -n /tmp/backup.lock sleep 5 & $ flock -n /tmp/backup.lock echo "running"; echo "exit: $?" exit: 1
Managing crontabs
Root can manage the crontab of any user with -u, and
-r removes the crontab.
sudo crontab -u alice -l crontab -r
The crontab -r command deletes all jobs without asking; since
r is next to e on the keyboard, it is wise to keep a
backup with crontab -l > crontab.bak. Access to cron can be
restricted with the /etc/cron.allow and
/etc/cron.deny files.
$ sudo crontab -u alice -l no crontab for alice $ crontab -r $ crontab -l no crontab for jano
Troubleshooting
When a job does not run as expected, check the following:
- Is the cron daemon running? Check
systemctl status cron(the service is calledcrondon Fedora and RHEL). - Did cron start the job? Look at the log with
journalctl -u cronorgrep CRON /var/log/syslog. - Does the job work in a minimal environment? Test it with
env -i /bin/sh -c 'command'. - Are percent signs escaped and paths absolute?
- Is the time zone correct? Cron uses the system time zone.
On systemd-based distributions, systemd timers are an alternative to cron with built-in logging and dependency handling.
Best practices for cron
- Use full paths: Do not rely on the
PATHof your login shell. - Log everything: Redirect output with
>> file 2>&1. - Put logic in scripts: Keep crontab lines short and test the scripts by hand.
- Avoid overlaps: Wrap long jobs in
flock. - Back up the crontab: Keep it in a file under version
control and install it with
crontab file.
Source
In this article, we have scheduled jobs with cron, examined the cron
environment, logged job output, worked with system cron directories, and
prevented overlapping runs with flock.
Author
List all Linux tutorials.