Schedule a Safe User Cron Job with crontab
The crontab command will happily install a broken schedule with no complaint if you skip the dry run. 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. 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.
Before you start: know which crontab you are changing
A user crontab runs commands as its owner. It is not the same file as /etc/crontab or a file in /etc/cron.d: those system files carry an extra username field and normally require elevated privileges. This guide changes only your own crontab, so sudo is not needed.
Never 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 leave 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, which 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 you do not need it 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*/15in the minute field means minutes 0, 15, 30 and 45. - Day fields are an OR, not an AND. 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. It 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 tries VISUAL, then EDITOR, then /usr/bin/editor. Before installing anything, prepare this line in a temporary file so a dry run can check it first:
$ 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, deliberately visible and low impact. For a real task, replace /usr/bin/date and the output path with absolute paths you have checked yourself. Cron supplies only a small environment: SHELL, HOME and LOGNAME are set, but shell startup files are not read, so do not assume your interactive PATH, aliases or working directory carry over.
Checkpoint: syntax-check without changing the table
Run Debian's dry-run mode:
$ crontab -n "$tmp_cron"
crontab: installing new crontab
That success message is slightly counter-intuitive: with -n, the file is checked and nothing is actually written. If you are testing on an account that already has entries, verify the existing table is unchanged:
$ 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, or at least partially refused, crontab.
4. Install and verify the entry
Installing a file replaces the current user's entire crontab. That 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, which is fine. If the installed output is not what you intended, restore the saved table with crontab "$backup_cron". Remove the temporary files once 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, since 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 tacked onto a command or environment assignment is passed through as part of that line, not treated as a cron comment.
6. Remove or undo the job
Warning
Removing a crontab is destructive. crontab -r deletes every entry for the selected user, so do not use it merely to remove one 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 actually read the prompt:
$ crontab -i -r
Recovery
To undo that removal, reinstall a known-good backup with crontab "$backup_cron". Root can manage another account with crontab -u ACCOUNT ..., but treat that as an explicit administrative action, since it changes that user's scheduled commands.
Done means
- Dry run passed.
crontab -n FILEaccepted the intended file without installing it. - Table confirmed.
crontab -lshows the expected entry and no accidental replacement. - Paths and output deliberate. The command uses checked absolute paths, appropriate permissions and deliberate output handling.
- Recovery understood. You know how to restore the saved table and have not used
crontab -rcasually.