The Proper Way of Adding a Cron Job

Until not so long ago, to add my own cron jobs I always had the habit of modifying /etc/crontab on my FreeBSD system which turned out to be wrong. In simple words, there are two types of crontab files:

  • System crontab which should not be altered due to the troubles it cause during FreeBSD upgrades
  • User crontab which has one less column than the system crontab file

So, I found out that the proper way it should be done is using crontab uitlity as it follows:

To create a crontab for current user:

$ crontab -e

To create a crontab for a specific user (e.g. babaei):

$ crontab -e -u babaei

This should create the user crontab files inside /var/cron/tabs/ (e.g. /var/cron/tabs/babaei) and open up your system’s default text editor which in my case is nano. It is better to avoid modifying these files directly and always modify them through crontab -e since you don’t have to restart the cron service manually this way. By the way, -e stands for editor mode.

This is a typical root user crontab file I use on one of my production servers (as you can see there is no who column since it’s useless in user’s own crontab file format):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
MAILTO=""

# Order of crontab fields
# minute    hour    mday    month   wday    command

# At Boot
@reboot     /root/.cron/spawn-gitlab.sh
@reboot     /root/.cron/spawn-trac-instances.sh

# Automatic daily backup
@daily      /root/.cron/backup.sh

# Agilo for trac
# Every 15 minutes
*/15       *       *       *       *        /root/.cron/resync-trac-repos.sh

# CloudFlare IP Ranges Automatic Update
# Every 24:00 hours at 01:00am UTC
00      01      *       *       *       /root/.cron/cloudflare-ip-range-updater.sh

# GeoIP updater
# every 7 days at 00:30am UTC
30      00      1,8,15,22,28    *       *       /root/.cron/geoupdater

To list the cron jobs in the crontab for the current user at any time, use the following command:

$ crontab -l

Or for a specefic user you can try this one (e.g. babaei):

$ crontab -l -u babaei

As you can see I added a few environment variables to the beginning of my crontab file. This is due to the fact that your cron scripts may not run properly due to executable path issues (e.g. you forgot to use absolute paths for commands you called in your scripts, so it won’t be able to find those commands you used in your scripts). If you want to test whether your scripts run properly or not, you should replicate the environment that would be used to run your scripts by testing them with a limited set of environment variables set by cron:

$ env -i \
    SHELL=/bin/sh PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin \
    HOME=/root LOGNAME=Charlie \
    /path/to/your/scriptname.sh

To see what these variables such as HOME, LOGNAME, MAIL, PATH and SHELL do, please refer to crontab’s manpage or documentation.

I almost forgot to mention that, there is another common pitfall when you are going to run your script or binary executables through a crontab. And that is, they should have executable permission which can be achieved by one of the following commands:

Make it executable for the owner of the file:

$ chmod u+x /path/to/your/executable

Make it executable for everyone:

chmod a+x /path/to/your/executable
floss  foss  freebsd  gnu  linux  unix