jason @ he

Scheduling cron jobs with crontab

There are tasks that need to be done regularly. Once the tasks are automated with a script it can be scheduled to run periodically using `crontab`.

This article covers the scheduling syntax, using the crontab command, and creating and debugging cron jobs.

The crontab command

Crontab is a file specific to the user that lists the cron jobs that will run and when they will run. Although crontab is a file, it isn't meant to be edited directly. We use the crontab -e shell command to edit our crontab. See the article about using the shell if you are unfamiliar with shell commands.

Editing Crontab

The -e option is for editing the crontab. The system's default editor is used if one hasn't been specified. You can specify a particular editor to use with an environment variable. For example, to use VIM:

EDITOR=vim crontab -e

Or if you prefer the nano editor:

EDITOR=nano crontab -e
Side Note

The editor can be changed permanently by using the select-editor command. Then you won't need to specify the editor in future commands.

% select-editor

Select an editor.  To change later, run 'select-editor'.
  1. /bin/nano        <---- easiest
  2. /usr/bin/vim.basic
  3. /usr/bin/vim.tiny
  4. /usr/bin/emacs25
  5. /usr/bin/emacs23
  6. /bin/ed

Choose 1-6 [1]: 2

Listing Crontab

Each line of the crontab is a cronjob. Cronjobs have a specific syntax we will go over soon. Lines starting with # are comments. Once we have edited the crontab and added a cronjob, we can see it with crontab -l

% crontab -l
#
# Example crontab
#

# Generate Account Mail
43 9 * * * echo "test"

# Generate mail and send to MAILTO address
MAILTO=jason@example.net
00 6 * * *  echo "test"

# Supress output so no mail is sent
7 3-23/4 * * * echo "test" >> /dev/null 2>&1

Interpreting the cronjobs

There are three cronjobs in the crontab above. The first part of each cronjob specifies when it will run. More on that later. The second part is the command that will be executed.

The first job is scheduled for 43 9 * * * and runs the command echo "test", which just outputs test. Since there is output, an email will be sent to the local mailbox.

The second job is scheduled for 00 6 * * * and runs the same echo "test" command, but this cron has MAILTO set so that the email is sent to jason@example.net instead of the default mailbox.

The third job is scheduled for 7 3-23/4 * * * and runs the same echo "test" command again, except all output is redirect to /dev/null. In other words, the output is discarded and no email is generated.

An Important Note!

Cronjobs will email the output of the command to the local mailbox. This can be useful for debugging cronjobs or generating reports, but often you don't want your local mailbox filling up with emails from cronjobs, especially if the cronjobs run frequently.

Output can be redirected to /dev/null to prevent emails from being generated. Keep in mind, you might be supressing errors that are occurring.

Crontab Scheduling

The first part of each cronjob sets the times that it will run.

There are five time intervals: minute, hour, day of the month, month, and day of the week. An asterisk means, "at every interval". So * * * * * would indicate that the cronjob runs every minute of every hour of every day of every month, every day of the week.

Replacing an asterisk with a number indicates the cronjob runs at that specific value. The first slot is minutes and ranges from 0-59. For example, 59 * * * * would run at minute 59 of every hour. 00:59, 01:59, 02:59, 03:59, 04:59, ..., 11:59, 12:59, 13:59, 14:59, ..., 22:59, 23:59. Remember that there are 24 hours in a day, not 12.

Each interval can use modifiers for finer control.

  • * is any value
  • , is a list of values
  • - is a range of values
  • / for step values
  • a number for an exact value

Here are more examples.

0 0-10 * * * runs on the hour from midnight to 10am and then doesn't run again until the following midnight.

0 0 */4 * 0 runs at midnight on every 4th day of the month that is a Sunday.

0,30 * */3,*/5 * * runs every half hour of every 3rd and every 5th day of the month.

Helpful Tools

The internet is full of useful tools for designing the schedule your cron needs. Crontab Guru has a simple interface that will output a schedule expression as a human-friendly sentence. See it in action with one of the examples from above: https://crontab.guru/#0,30_*_*/3,*/5_*_*

Crons

Now that we can write our cronjobs and understand scheduling, we can run cronjobs periodically. There are some gotchas to keep in mind. Test your cronjobs thoroughly.

"It Works For Me" Errors

cronjobs execute in a slightly different environment than when you execute a script directly. Just because a script is working for you does not mean it is guaranteed to work as a cron.

If a cron is not working correctly, a good start is to try to execute the command yourself. If it isn't working for you, there is a good chance it won't work as a cron. So make sure it does work for you.

But, if it isn't working as a cron, you'll need to do further troubleshooting.

Make sure that the cron is in the appropriate working directory. You can have your cron change to the correct directory first. For example, * * * * * ./my-script might not work. I can call ./my-script and it works, but the cron can't. What is happening?

The cron might be in a different working directory than me. I can modify the cron to change to the correct directory first like so: * * * * * cd /home/jason/scripts && ./my-script

You might be using a different binary than the cronjob. * * * * * php my-php-script.php calls the php binary, but which PHP? Try to specify full paths to the exact binary you want to use to leave no doubt.

* * * * * /usr/bin/php my-php-script.php is better, but it still might not be the PHP you expect. For this example, I would look at which PHP Apache is using and set the cron to use that. * * * * * /usr/bin/php8.2 /home/jason/scripts/my-php-script.php -- There. Now my cron will use the same PHP that my website is using, with the same version, the same extensions, and the same configuration. And it is definitely running the correct PHP script.

"Wrong Environment" Errors

You may have environment variables set that the cronjob doesn't. Environment variables can be specified in the command if necessary or for troubleshooting.

* * * * * NODE_ENV=staging /home/jason/.nvm/versions/node/v16.20.2/bin/node build.js --quiet

"The cronjob never runs" Errors

You'll often see cronjobs that end in >> /dev/null 2>&1, which sends all standard output and all error output into oblivion so you don't get emails about your cronjob output. That is fine if you can't prevent the output from occurring and it is expected and you don't want an email about it.

If it appears like the cronjob is not running, the first step is to test it without redirecting output so that you can examine the output. There may be helpful errors that were being discarded.

The next item to check is the scheduling. Try setting a specific time for it to run like 00 9 * * *. That is a straightforward schedule -- 9AM. If it runs at 9AM, but wasn't running with your previous schedule, you will need to revisit your previous schedule. Maybe you are misinterpreting it, maybe there was a typo, or maybe the cron daemon was not running at that very specific time, like a scheduled reboot that happens just before your cron is set to run.

If the cron still doesn't run with a simpler schedule, check that the cron daemon is running. Cronjobs won't happen without crond.

When all else fails reduce crons to the basics, like 1 * * * * whoami. Make sure crons run at all. Test your assumptions. In rare cases the crontab log might shed light on an issue.

Back to Article Listings
Glossary