Getting Cron jobs to actually run

Cron is the utility that allows you to schedule repetitive tasks on Unix-like systems. Mostly I use it to schedule backups: I have a little script that sends a copy of my database to Amazon S3 every day. It seems like it should be straightforward, but there’s a gotcha that I didn’t understand for a long time, and I think I’m not alone.

You control Cron by editing your Crontab file with this command:

$ crontab -e

Crontab’s syntax is terse: numbers separated by spaces to indicate time, and then a command to run:

35 22 * * * <command>

For instance, that means “Run this command every day at 10:35 PM”. Not difficult to grasp conceptually. But I always had trouble getting cron jobs to run properly because almost no one tells beginners that the <command> you invoke must be an absolute path, not a relative one.

Here’s a common situation to illustrate what I mean:

  1. You’re logged in to your machine as, say, a user named webmaster.
  2. You have your backup script, backup.sh, saved in your home directory.
  3. You type ~/backup.sh into the command line.
  4. The script runs successfully!

So intuitively, it feels like this Crontab line should work:

35 22 * * * ~/backup.sh

…but it won’t, because when Cron runs, it doesn’t assume that user’s identity. It’s not starting its operation from their home directory, so the relative path doesn’t work.

Instead, you have to use the full, absolute path to the script, starting from the root directory:

35 22 * * * /home/webmaster/backup.sh

I was confused for, literally, years about why I couldn’t get cron jobs to run, and this was almost always why. Many of the popular Crontab tutorials and blog posts out there, even otherwise fairly thorough ones, use absolute file paths in their examples — but they very rarely explain why, or that this is effectively a requirement, not just a convention.