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
FlagEffectPortable
-lprint the crontabyes
-eedit and install on saveyes
-rremove the crontabyes
-u useroperate on another user's crontab, root onlyyes
-iwith -r, prompt before removingcronie, Debian
-Tvalidate a file's syntax without installingcronie
-sappend the SELinux MLS levelcronie
-c -ncluster host selectioncronie
-Vversioncronie

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

PathContents
/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/crontabsystem 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.