Home / Alt manpages / crontab(1)

  • crontab(1)
  • User command
  • linux

Schedule a Safe User Cron Job with crontab

In about 15 minutes, you will create a per-user cron entry, check its syntax without installing it, install it, and verify the saved table. The examples use the Vixie Cron implementation supplied by Debian's cron package, version 3.0pl1-184ubuntu2 on the machine used for this guide. You need a normal shell account and a command that is safe to run more than once.

Checkpoint: know which crontab you are changing

A user crontab runs commands as its owner. It is not the same as /etc/crontab or a file in /etc/cron.d. The latter system files have an extra username field and normally require elevated privileges. This guide changes only your own crontab, so do not use sudo.

Do not edit /var/spool/cron/crontabs directly. The crontab command checks syntax and writes the managed file with the permissions cron expects. If you are inside su, use -u deliberately: the local manual warns that su can make the selected account confusing.

1. Inspect the current table

List the entries belonging to the account running the command:

$ crontab -l

On a new account, the command may report that there is no crontab for the user. That is a normal starting point. Debian's command normally omits its installation header from this output; setting CRONTAB_NOHEADER=N asks for the header, but it is not needed for editing or checking.

2. Understand the five time fields

A user entry has five time fields followed by the command:

minute hour day-of-month month day-of-week command
FieldValuesExample
Minute0-5915
Hour0-236
Day of month0-311
Month0-12, or the first three letters of a month*
Day of week0-7, where 0 and 7 are Sunday, or a name1-5

An asterisk means every permitted value. Ranges are inclusive, lists use commas, and steps use /, so */15 in the minute field means minutes 0, 15, 30 and 45. When both day-of-month and day-of-week are restricted, this implementation runs the command when either field matches, not only when both match. That is a frequent source of unexpected extra runs.

3. Prepare a harmless entry

Use an editor with crontab -e for an interactive change. The command uses VISUAL, then EDITOR, then /usr/bin/editor. Before installing anything, prepare this line in a temporary file so the dry run can check it:

$ tmp_cron=$(mktemp)
$ printf '%s\n' '*/5 * * * * /usr/bin/date >> /tmp/cron-check.log 2>&1' > "$tmp_cron"
$ cat "$tmp_cron"
*/5 * * * * /usr/bin/date >> /tmp/cron-check.log 2>&1

This writes a timestamp every five minutes to a file in /tmp. It is deliberately visible and low impact. For a real task, replace /usr/bin/date and the output path with absolute paths you have checked. Cron supplies a small environment: SHELL, HOME and LOGNAME are set, but shell startup files are not read. Do not assume your interactive PATH, aliases or working directory.

Checkpoint: syntax-check without changing the table

Run Debian's dry-run mode:

$ crontab -n "$tmp_cron"
crontab: installing new crontab

The success message is slightly counter-intuitive: with -n, the file is checked and nothing is written. Verify that the existing table is unchanged if you are testing on an account that already has entries:

$ crontab -l

Fix any diagnostic before continuing. Keep the final newline in the file; cron treats a missing newline at end of file as a broken, at least partially refused, crontab.

4. Install and verify the entry

Installing a file replaces the current user's entire crontab. This is a state-changing operation, so preserve the existing table first:

$ backup_cron=$(mktemp)
$ crontab -l > "$backup_cron"
$ crontab "$tmp_cron"
$ crontab -l
*/5 * * * * /usr/bin/date >> /tmp/cron-check.log 2>&1

If there was no existing table, the backup command may print an error and create an empty file; that is fine. If the installed output is not what you intended, restore the saved table with crontab "$backup_cron". Remove the temporary files when you have finished checking:

$ rm -f "$tmp_cron" "$backup_cron"

Wait at least five minutes, then inspect the result:

$ tail -n 3 /tmp/cron-check.log

If the file is absent, check the schedule, absolute command paths, permissions and the cron service logs for your distribution. Cron examines entries once per minute, so a job added just after its scheduled minute waits for the next matching minute.

5. Control output and special characters

If a command writes output and cron has a reason to send mail, MAILTO selects the recipient. Set MAILTO="" to suppress mail, or redirect both standard output and standard error to a log you own. Keep log growth in mind; cron does not rotate arbitrary files.

In a command, an unescaped percent sign becomes a newline, and everything after the first one is sent to standard input. Escape it when the command needs a literal percent, for example:

0 7 * * 1-5 /usr/bin/date +\%F >> /tmp/workday-date.log 2>&1

Comments must occupy their own line. Text after a command or environment assignment is passed through as part of that line, rather than being treated as a cron comment.

6. Remove or undo the job

Removing a crontab is destructive: crontab -r deletes every entry for the selected user. Do not use it merely to remove the example. Restore the backup, or edit the table and delete only the unwanted line:

$ crontab -e
$ crontab -l

For a deliberate full removal, use the confirmation variant and read the prompt:

$ crontab -i -r

To undo that removal, reinstall a known-good backup with crontab "$backup_cron". Root can manage another account with crontab -u ACCOUNT ..., but that should be an explicit administrative action because it changes that user's scheduled commands.

Done means

  • crontab -n FILE accepted the intended file without installing it.
  • crontab -l shows the expected entry and no accidental replacement.
  • The command uses checked absolute paths, appropriate permissions and deliberate output handling.
  • You know how to restore the saved table and have not used crontab -r casually.