Quick Links

The cron utility is used for running scripts and commands at regular intervals, and at specific times and dates. It's built into most Linux distros, and provides a very useful way to schedule tasks on your server.

cron is an automation tool, so anything that you run on a regular basis can likely be switched over to a cron job. If you wanted to make regular daily backups, or restart a service once a week, cron can do that.

How Does Cron Work?

Cron jobs are defined in a file called the crontab. This file is unique to each user, and while they're usually located under

        /var/spool/cron/crontabs
    

, they're not intended to be edited directly. Instead, you edit them through the

        crontab
    

 command:

crontab -e

This will open your user's crontab in your default CLI text editor. If this got you stuck in vim, you can change the default editor by running select-editor on some systems, or by adding export VISUAL=micro; to your ~/.bashrc (or shell equivalent).

By default, your crontab is likely blank, so you'll have to add jobs manually. A cron job is usually formatted as:

minute hour day month weekday <command-to-execute>

The command to execute can be anything, including shell scripts

The values for each time variable can be wildcards, which cron will interpret as always true. For example, if you wanted to run a job fifteen minutes past every hour, every day, you'd use:

15 * * * * command

This will run on minute 15 of each hour, each day, each month, no matter the day of the week.

If you wanted to run a job at 5:30 PM on every Friday, you'd use:

30 17 * * 5 command

cron also supports ranged and stepped values. Ranged values include every value within a specific range. If you wanted to run a job every hour while you're working, you'd use:

0 9-17 * * 1-5 command

From 9 AM to 5 PM, Monday through Friday. Similarly, you can define a list of values by separating them with commas.

Stepped values run at more specific intervals. You can run jobs every two hours using:

0 */2 * * * command

Keep in mind that the clock starts at midnight, 00:00. Your job will run once then, then at 2 AM, then at 4 AM and so on.

Some versions of cron support more syntax, such as @daily to run once a day, or @reboot to run on every reboot, though your distro may vary.

If you want an easy way to debug cron syntax, there are many online editors such as crontab.guru that will show you what your schedule expression means in layman terms, i.e. "At minute 0 past every 2nd hour."

Making Sure Cron is Working

Cron doesn't show you any indication that it's running your jobs. If you'd like to know if it's working, you'll need to redirect the output manually. You can do this by piping the output to a log file:

* * * * * echo "test" >> logfile 2>&1

The >> operator appends the output to a file, and the 2>&1 operator makes sure to include stderr in the output. The bash equivalent would just be &>> in place of the >>, but that may not work on every distro, since cron uses /bin/sh by default.

Cron can be configured to send emails with the output of jobs. It actually does this by default with your user account's default email address, but it likely isn't configured properly. To get email working, you'll need a mail agent set up and configured on your server, which will allow you to use the mail command to send emails. Then, place this line above your cron jobs in your cron job:

MAILTO="yourname@gmail.com"

Now, any output from any job that goes to stdout (e.g., it isn't piped somewhere) will be sent through mail to the address you specified. You can test this by adding a temporary job to echo to stdout every minute:

* * * * * echo "cron mail is working!"

Just make sure to turn that job off once it is working, or it will get annoying very quickly.

Cron Uses /bin/sh By Default, Not Bash

You may be using a different shell than what cron runs your jobs in. Bash (/bin/bash) is a common shell on most distros, and is an implementation of sh. The /bin/sh file is a symlink to an sh implementation, but it isn't always bash. On Debian based systems like Ubuntu, and on macOS, /bin/sh links to dash by default.

The end result of this confusion is that your scripts might not run the same, and your PATH variable (among others) may not be configured properly. You can get around this in a few ways, and your distro's variant of cron may work differently, so you may have to try a couple:

  • Adding  SHELL=/bin/bash to the top of your crontab. You may also have to add BASH_ENV="/root/.bashrc"  for it to read your bash profile.
  • Putting /bin/bash before the command in each job. You may need to set the BASH_ENV variable even if you're not switching to bash globally.
  • Using the bash shebang #!/bin/bash at the top of each script. This requires you to have each job in its own script file.

In any case, you should debug your cron jobs by setting them to run a few minutes ahead before considering them to be reliable.

Manually Updating Your Crontab

If you'd rather not manage your crontab through crontab -e, there's better method you can use. The command crontab -l will display the contents of your crontab, and you can pipe this out to a file:

crontab -l > my_crontab

And then upload that file onto another system, and 'install' it with:

crontab my_crontab

This way, you won't cause any errors by editing it directly, as it's still loaded in through cron.