sysadmin · difficulty ◆◆
crontab — schedule recurring jobs
Teach the system to run your jobs while you sleep.
You could ssh in at 3am to run your backup by hand — or you could teach cron to do it forever. One five-field line is the difference between babysitting servers and letting them run themselves.
AI-generated draft · reviewed and editedThis text was generated with AI as a first draft, then reviewed and edited by the author, who holds editorial responsibility for it.$ crontabWhat it does
crontab is the control panel for cron, the daemon that runs commands on a schedule. Each user owns a crontab file: a list of lines, one per job, each with five time fields (minute, hour, day-of-month, month, day-of-week) followed by the command to run. You edit it with `crontab -e`, list it with `crontab -l`, and remove it with `crontab -r`. The daemon checks these files every minute and fires anything that matches. It is how log rotation, backups, certificate renewal, and cleanups keep running without anyone touching them.
Why it matters
Any task that should happen on a schedule — nightly backups, clearing /tmp, renewing a TLS cert, a daily report — is a cron job. The value is reliability: once the line is in, it runs every time, whether or not you remember. The gotcha is that cron does not load your shell profile: it runs with a bare environment (limited PATH, no your aliases, no your exports), which is why jobs that work when you type them fail mysteriously in cron. Understanding that one mismatch saves hours of debugging.
Shortcuts, dry runs, and system crontabs
Two conveniences are worth knowing. First, the @ shorthands: instead of `0 0 * * *` write @daily (or @midnight), instead of `0 0 1 1 *` write @yearly (or @annually), and @hourly, @weekly, @monthly and @reboot work the same way. @reboot fires when the cron daemon starts, which can be before the network or other services are up, so do not put anything in it that needs the network. Second, `crontab -n file` checks syntax without installing: it prints 'The syntax of the crontab file was successfully checked.' or refuses with the offending line, which is the safe way to test a schedule a script generated. Both forms are also worth knowing because cron is not the only scheduler on the box: /etc/crontab and /etc/cron.d/ hold system-wide jobs whose lines carry a sixth field naming the user to run as, and /etc/cron.allow and /etc/cron.deny decide who may use crontab at all.
Example
$ crontab -l# m h dom mon dow command
PATH=/usr/local/bin:/usr/bin:/bin
HOME=/home/kmail
# Nightly backup at 02:30
30 2 * * * /home/kmail/scripts/backup.sh >> /var/log/backup.log 2>&1-l lists the current user's crontab. The header line is just a comment reminding you the fields are minute, hour, day-of-month, month, day-of-week.
$ echo '0 9 * * 1-5 /home/kmail/scripts/daily-report.sh' | crontab -$ crontab -l | tail -2
0 9 * * 1-5 /home/kmail/scripts/daily-report.sh`crontab -` installs a crontab from stdin. Here a job runs at 09:00 on weekdays (Mon-Fri). This is the scriptable way to add jobs — safer than typos in an interactive editor.
$ crontab -e# opens in your $EDITOR; add a line and save
# This job clears the temp dir at 04:00 every Sunday:
0 4 * * 0 /usr/bin/find /tmp/kmail -type f -mtime +2 -delete-e opens today's crontab in your default editor (EDITOR, or /usr/bin/editor if that is unset). After you save, cron validates the file before installing it: a clean parse prints 'crontab: installing new crontab', and a bad line is refused with 'errors in crontab file, can't install.' — the previously installed crontab stays in place.
$ printf '0 3 * * /bin/true\n' | crontab -n -"-":0: bad day-of-week
errors in crontab file, can't install.A missing field is caught before anything is installed: the dry run names the line and the problem, and the crontab already installed is left untouched. The same command on a good line prints 'The syntax of the crontab file was successfully checked.'
Common flags
- -e
- edit the current user's crontab in $EDITOR
- -l
- list the current user's crontab to stdout
- -r
- remove (delete) the current user's crontab entirely
- -i
- interactive — prompt before deleting when used with -r
- -n
- dry run — check a crontab's syntax without installing it
- file | -
- install a crontab from a file, or from stdin with `crontab -`
- -u user
- target another user's crontab (root only; e.g. crontab -u www-data -l)
- *
- wildcard in a time field matches every value (e.g. `* * * * *` = every minute)
History
The name
The name comes from Chronos — χρόνος, the Greek word for time — and the first cron was Ken Thompson's, at Bell Labs: it was running by 1975 and shipped in Version 7 Unix in 1979, where it woke once a minute and read a single system-wide /usr/lib/crontab as root. The five-field format, minute field included, was already there. What came later was Robert Brown's multi-user cron, built at Purdue on an event list instead of a once-a-minute scan so that it could run jobs for every user rather than root alone; Keith Williamson turned the prototype into a production service that went into use at Purdue in late 1979 and later reached System V. Today cron variants (Vixie cron, cronie, dcron) ship on every major distro, and systemd can schedule too, but the crontab format has barely changed in half a century.
Why five fields
The five-field format — minute, hour, day-of-month, month, day-of-week — was designed in the 1970s for line-printers and tape backups, the recurring jobs of the day. It has a famous quirk: if both day-of-month and day-of-week are restricted (not *), the job fires when EITHER matches — a semantic trap that still trips people today. Cron predates every job scheduler since, and its core design lives on in Windows Task Scheduler and systemd timers.
Fun facts
Pros & cons
pros
- + Runs jobs forever with zero attention once the line is written
- + Present on virtually every Unix-like system — no install, no daemon to write
- + Simple five-field syntax that is the same everywhere and greppable in configs
cons
- − Bare environment (no PATH, no shell profile) causes silent failures for newcomers
- − No retry, no timeout, and overlaps can run simultaneously if a job is slow
- − The day-of-month + day-of-week OR semantics are a surprising trap
- − No logging built in — you must redirect output yourself
Takeaways
- 1Edit jobs with `crontab -e`; never hand-edit the file directly
- 2List what's running with `crontab -l`, delete everything with `crontab -r`
- 3Set PATH= at the top of the crontab and use absolute script paths — cron ignores your shell profile
- 4Redirect every job's output with `>> log 2>&1` or it disappears
- 5Dry-run a new schedule by setting the next slot to one minute out and watching the log
