cron Cheatsheet
Managing Crontabs
Use this cron reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
The four commands
crontab -l # list your crontab to stdout crontab -e # edit it in $VISUAL or $EDITOR crontab -r # DELETE it, no confirmation crontab file # replace it wholesale from a file crontab - # replace it from stdin crontab -u deploy -l # another user's, needs root
| Flag | Effect | Portable |
|---|---|---|
-l | print the crontab | yes |
-e | edit and install on save | yes |
-r | remove the crontab | yes |
-u user | operate on another user's crontab, root only | yes |
-i | with -r, prompt before removing | cronie, Debian |
-T | validate a file's syntax without installing | cronie |
-s | append the SELinux MLS level | cronie |
-c -n | cluster host selection | cronie |
-V | version | cronie |
crontab -r deletes everything, instantly
There is no prompt, no undo, and no -r for a single line. One typo, most often
crontab -r when crontab -e was meant, and every scheduled job on that account
is gone.
crontab -r # your entire crontab is now gone crontab -l # crontab: no crontab for deploy
Defenses, cheapest first:
# 1. Always take a backup before touching it crontab -l > ~/crontab.$(date +%F).bak # 2. Use -i so -r asks first (cronie and Debian) crontab -r -i # 3. Alias the dangerous form out of existence alias crontab='crontab -i'
cronie also keeps a safety net of its own: on edit or delete it writes the
previous crontab to $XDG_CACHE_HOME/crontab/crontab.bak, defaulting to
~/.cache/crontab/crontab.bak. That is one level deep only, so it survives one
mistake, not two.
Treat the crontab as a file in version control
Editing live with -e means the only copy of the schedule is in a spool
directory nobody backs up. The better workflow is a file in the repo.
# repo: ops/crontab.deploy crontab -T ops/crontab.deploy # validate first, cronie crontab ops/crontab.deploy # install, replacing everything crontab -l | diff - ops/crontab.deploy # confirm what is live
Because installing replaces the whole crontab, the file must contain every job. To add one line non-interactively without losing the rest:
( crontab -l 2>/dev/null; echo '0 3 * * * /usr/local/bin/job.sh' ) | crontab -
To remove a line:
crontab -l | grep -v '/usr/local/bin/job.sh' | crontab -Both idioms depend on crontab -l succeeding. Note the 2>/dev/null, which
turns "no crontab for user" into an empty input instead of aborting the pipeline.
crontab -e, and the editor that will not work
-e uses $VISUAL, then $EDITOR, then a compiled-in default, usually vi or
Debian's sensible-editor.
EDITOR=nano crontab -e
sudo update-alternatives --config editor # Debian and Ubuntu- The BSD and macOS crontab -e requires an editor that edits in place. Any
editor that unlinks and recreates the file, which includes some GUI editors and
code without --wait, cannot be used.
- On save, crontab parses the file. A syntax error leaves the old crontab
installed and offers to re-edit, which is why -e is safer than piping a file
in blind.
- -e operates on a temp copy, so two people editing the same crontab at once
will have one silently overwrite the other.
Where crontabs live, and how cron notices changes
| Path | Contents |
|---|---|
/var/spool/cron/crontabs/<user> | user crontabs, Debian and Ubuntu |
/var/spool/cron/<user> | user crontabs, RHEL, Fedora, Arch |
/usr/lib/cron/tabs/<user> | user crontabs, macOS and BSD |
/etc/crontab | system crontab, has an extra user field |
/etc/cron.d/* | drop-in system crontabs, same format as /etc/crontab |
Never edit those spool files directly. They are mode 600, owned by the user,
and cron notices changes by watching the spool directory's mtime, which
crontab updates for it. A hand-edited file may be ignored, and cronie with
inotify support will not pick up a change made through a symlink.
sudo systemctl reload crond # RHEL, Fedora, Arch sudo systemctl restart cron # Debian and Ubuntu sudo launchctl kickstart -k system/com.vix.cron # macOS
Reloading is almost never needed. Cron rereads changed crontabs within a minute
on its own. Restarting the daemon does re-trigger every @reboot line, which is
occasionally a useful side effect and more often a surprise.
Who is allowed to use cron
/etc/cron.allow /etc/cron.deny # Linux /usr/lib/cron/cron.allow /usr/lib/cron/cron.deny # macOS
The precedence is fixed and catches people out:
1. If cron.allow exists, you must be listed in it. cron.deny is ignored.
2. Otherwise, if cron.deny exists, you must not be listed in it.
3. If neither file exists, cronie allows only root, while Vixie cron and most
distributions allow everyone.
One username per line, no whitespace. Root always wins regardless. On macOS a
further layer applies: Full Disk Access must be granted to cron in System
Settings, or jobs that touch protected paths fail with permission errors that
appear nowhere in a log.
Quick audit of everything scheduled on a box
# every user's crontab for u in $(cut -d: -f1 /etc/passwd); do crontab -l -u "$u" 2>/dev/null | sed "s/^/$u: /" done # system schedules cat /etc/crontab ls -l /etc/cron.d/ /etc/cron.{hourly,daily,weekly,monthly}/ # systemd timers, which cron users routinely forget to check systemctl list-timers --all
That set is the honest answer to "what runs on this machine", because a schedule can live in any of the four places and no single command lists them all.