Enabling cron in OSX 10.10 Yosemite

Strangely, one of the earliest things I learned in unix computing was crontab -e. In fact, it would not be an exaggeration to say, the reason I learned to use vim in the first place, was so I could fire off automated jobs in cron while I slept (Don’t ask. Mine was a complex childhood. :-) ).

I’ve never been a fan of Apple’s launchd replacement on the OSX operating system.

cron has its shortcoming to be sure, but it’s unix-y, simple, and let’s face it… gets things done, plus, it works on millions of systems worldwide. While I get that Apple wanted to replace the historical soup that was initd (amongst other things), I’ve always felt launchd was painful. Admittedly, this might just be because I’ve always found cron so unbelievably fit-for-purpose when you actually know how to use it.

Apple seems to have gone out of its way to make cron a bit tricky for you to enable, but after some substantial googling and experimentation, I thought I’d write it down so you don’t have to go through the same rigmarole.

What You Don’t Get

Before you enable your crontab though, just a few caveats on what you do get with launchd, just in case you’d rather use one of the GUI apps to interface with that and solve the same problem, instead of going this route.

The main thing, is that launchd tasks will check upon upon waking from sleep, whether a specific tasks was scheduled to run during sleepy time ran, and then run it if it was supposed to (note: this does not apply if the system was off or similarly during that time, afaik)

The other thing you can do is to use launchctl to write background tasks that run, so you could get around things this way as well:

launchctl submit -l task1 — $HOME/bin/task1.sh foo bar

But personally, I find the XML configuration of the plists painful and a step down from the simplicity of a crontab. YMMV.

Enable system cron

In a nice touch though, you can get launchd to fire up the system (root) cron, if you simply do this:

🚀  ~ sudo touch /etc/crontab

You probably will need to reboot in case launchd doesn’t want to launch it, but doing that means that launchd will see cron and launch it on startup.

(nb: unless you really know what you’re doing do not mess with the /etc/crontab file. Run all your cron jobs under your user account.)

Getting cron running for users under your crontab

The easiest way after you’ve got the above goingi, is to simply create a file called .crontab in your home directory. Cron will automatically pick this up for you and start running it if it’s present on startup.

The canonical way to do this is the following command:

🚀  ~ crontab -e

which will dump you into the old-school-y (yet surprisingly resurgent) editor vim. If you’ve never used it before, well… it may be a bit of a shock. I certainly recommend investing the time to learn, but assuming you are in oh my god, just tell me how to get this working! mode, type this in the command line before executing crontab -e

🚀  ~ export EDITOR=nano

(nb: if you’re stuck in vi at the moment, hit :q! to exit and get back to the command line and do the above.).

Once you’re in the vi editor, it’s really just simple text editing.

For example, here’s the line I put in to do routine, weekly back ups of my Gmail using Gmail Vault (I’d recommend having the logging going in case you need to debug things.):

 15 13 * * 1 /usr/local/bin/gmvault sync -t quick -d /Users/me/mail_backup_dir
      me@.example.com > ~/mail_backup_dir/gmvault.log
      2> ~/mail_backup_dir/gmvault-error.log

(nb: all the above should be on one line in your crontab, not split over 3)

Admittedly, you could make this shorter without the logging, but I like having that in there for checking and goof proofing. What this cron does is run the gmvault quick sync command to a specified backup directory every Monday at 1:15 pm.

Exit vim, remembering to save the file (:wq), and then do a crontab -l to make sure it stuck. This should list the contents of the crontab file.

Normally, this should just work, but if it appears that cron still isn’t working you can also give it a kick with crontab ~/.crontab

Troubleshooting

And believe it or not, you may still not be there.

Again, some little quirks with Apple’s (admittedly fabulous) OSX may be tripping you up.

If you are in vim (the default for editing on crontab -e and do the funky :wq to write and quit and you get,

crontab: "/usr/bin/vi" exited with status 1

you will annoyingly find your changes have not been saved. What gives, right?

Apparently, this has to do with the way crontab wants temporary files to work and how it names them. Suffice it to say, there are a few ways around this. The one I added to my .vimrc since it seemed the least intrusive was:

set backupskip=/tmp/*,/private/tmp/*`

Fire crontab -e back up again after saving the above and any changes made to the crontab should be saved.

An even better solution someone posted on the Vim Wikia is a lot more specific in case you don’t like the idea of the above solution.

au BufEnter /private/tmp/crontab.* setl backupcopy=yes

There are a few other ways you can accomplish the same thing, but the others involve editing in multiple places or in the bash profile and are not as clean as this one which is fairly vim specific.

Denouement

And that, is how you get cron enabled and running on OSX Yosemite.

My cron tasks have been chugging away quite happily over the past few weeks and I have to admit I’m a lot happier for my OSX working the way I want. While I realize that a number of Linux distros have also attempted to replace the initd startup with their solutions, Upstart being the one most recognizable, I’m assuming cron is still the word in the Linux world. Glad to have it back on my OSX.