References: crontab.guru (build/check schedules), cronitor.io (job monitoring/alerting), Fedora: automating system tasks, DigitalOcean: how to use cron.
Install and enable¶
sudo dnf install crontabs # RHEL family (installs cronie, cronie-anacron, crontabs)
sudo apt-get install cron # Debian/Ubuntu
sudo systemctl enable --now crond # RHEL family; service is "cron" on Debian/Ubuntu
sudo systemctl status crond
Schedule syntax¶
* * * * * command to run
│ │ │ │ │
│ │ │ │ └── day of week (0-7, both 0 and 7 = Sunday)
│ │ │ └──── month (1-12)
│ │ └────── day of month (1-31)
│ └──────── hour (0-23)
└────────── minute (0-59)
Per-user crontabs¶
crontab -e # edit your own
crontab -l # list your own
crontab -u alice -e # edit another user's (root only)
crontab cron.txt # install from a file (replaces the current crontab!)
crontab -u alice cron.txt # same, for another user
crontab -e is always the right way to install a job. Directly copying a file into /var/spool/cron/ (or crontabs/) with the right name and ownership can work but skips validation, and cron may not notice the change until it rereads spool timestamps.
The system crontab and cron.d¶
/etc/crontab is the system-wide crontab; unlike per-user ones it has a username field:
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# minute hour dom month dow user command
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 )
Individual job files with the same syntax go in /etc/cron.d/ (handy for packages, or for keeping one job per file under version control).
A backup schedule example¶
# /etc/cron.d/nightly-backups
MAILTO=root
00 02 * * * appuser rsync -a --delete /home/appuser/Documents /mnt/backup/Documents
15 02 * * * appuser rsync -a --delete /home/appuser/Projects /mnt/backup/Projects
00 04 * * * root rsync -a --delete /var/lib/myapp /mnt/backup/myapp
See rsync for the flags. MAILTO needs working local mail to actually deliver; otherwise redirect output explicitly (>> /var/log/job.log 2>&1) instead of relying on cron's mail.
Newer alternative: systemd timers¶
On systemd distributions, a .timer unit paired with a .service unit gives dependency ordering, logging via journalctl, and OnCalendar= schedules that also understand cron-like syntax. For anything more than a couple of simple jobs it is worth learning systemd.timer(5).
Troubleshooting¶
- Cron jobs run with a minimal environment: no login shell, sparse
PATH. Use full paths to binaries, or source the needed profile at the top of the script. - Check
journalctl -u crond(or-u cron) or/var/log/cronfor whether a job actually fired. run-partsskips scripts without the executable bit and skips names containing dots (other than a leading exclusion list), which trips people up when a script namedbackup.shsilently never runs from/etc/cron.daily/.