Scheduling with CRON

21st February 2021

Systems

In the Linux world we use CRON jobs to schedule tasks to run at certain times, days or months.

An example for cron jobs are used is to run daily backup scripts.

Cron reads from a few places in Linux:

  • /var/spool/cron/crontabs
  • /etc/crontab
  • /etc/cron.d/
  • /etc/cron.{daily,hourly,weekly,monthly}/

Cron files are commonly known as crontab files, the crontab files in the first location in the above list should be edited with the crontab command.

Every minute cron runs and processes the list of known tasks and runs them if required. The only exception to this is when the system time changes by less than 3 hours, for example when daylight saving time starts and ends. In the case of time moving forward, jobs that should have run in the skipped time will be run, for the case of time moving backwards, jobs in the repeated time will not be re-run.

When writing command to be run you should use the full path to the command, for example, /usr/bin/ssh

The crontab command

By default on Debian based systems any user can use the crontab command. This can be controlled by a /etc/cron.allow or /etc/cron.deny file.

If the /etc/cron.allow file exists then your username must be in this file to use crontab command.

If the /etc/cron.deny file exists then your username must not be in this file to use crontab command.

If neither files exist then the systems default configuration will be used, this is either all users or just super users that are allowed to use the crontab command.

Some useful command parameter are:

  • -u will define the user of the crontab you want to update, if this is omitted it will use the username of the user running the command
  • -l will print the content of the users crontab to standard output
  • -r will remove the users crontab
  • -e will open the users crontab in an editor, such as nano or vim

Crontab Format

Cron decides when to run commands based on when the minute, hour and month fields match the current time, and when either one of the two day fields, day of month or day of week, matches the current time.

This above point means that if you schedule a command to run on the 4th and 20th of every month and on Thursdays, then it will run on the 4th and 20th of the month and every Thursday.

There are a few ways to define values:

  • Wildcard / catch all values are indicated by the asterisk (*)
  • Number ranges can be used with numbers being separated with the hyphen (-) and is inclusive, for example the range 14-20 would include 14,15,16,17,18,19,20
  • Lists are also accepted, for example "1,2,8,9" and "2-8,14-20"
  • Step values can be used with ranges, for example to run a command on every other hour use */2, 0-23/2 will also do the same
  • Names of the week and month can be used, Sun-Sat and Jan-Dec, these can not be used in ranges or lists however

When writing a crontab entry follow this format, in the example this will run the ls command at 8am on the 19th of every month

Minute   Hour     Day of Month    Month                 Day of Week          Command    
(0-59)   (0-23)   (1-31)          (1-12 or Jan-Dec)     (0-6 or Sun-Sat)                
0        8        19              *                     *                    /usr/bin/ls

Crontab also exposes some special schedule strings

@reboot        Run once, at startup
@yearly        Run once a year, 0 0 1 1 *
@annually      same as @yearly
@monthly       Run once a month, 0 0 1 * *
@weekly        Run once a week, 0 0 * * 0
@daily         Run once a day, 0 0 * * *
@midnight      same as @daily
@hourly        Run once an hour, 0 * * * *

Gotchas

  • When cron runs a command it does not source any files that are in the users home directory, you will have to source them in the script being called
  • If the account does not have a shell defined in the /etc/passwd file then the cron will not run
  • If cron does not run check the daemon is running and check for any /etc/cron.allow or /etc/cron.deny files
  • You can not use % in the command section, you need to escape it with \% or use command substitution $()
  • Remember the point about day of month or day of week, if not set to * this will become a logical OR condition, only one value has to match the current time to be considered true to run the command

I hope this information help you to understand and write crontabs!