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.
The route
Jump straight to the step you need, or tick off Done means at the end.
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
| Field | Values | Example |
|---|---|---|
| Minute | 0-59 | 15 |
| Hour | 0-23 | 6 |
| Day of month | 0-31 | 1 |
| Month | 0-12, or the first three letters of a month | * |
| Day of week | 0-7, where 0 and 7 are Sunday, or a name | 1-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 FILEaccepted the intended file without installing it.crontab -lshows 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 -rcasually.