Skip to content

Goodbye cron task, hello launchd agent

On April 29th, 2005, Apple launched Mac OS X 10.4, aka Tiger. With Tiger came launchd, a new Unix-side job scheduling tool. launchd was intended to replace cron, the long-established (and quite cryptic) tool for such tasks.

And now, a mere nine-plus years later, I decided it was time to give up cron and move to launchd myself. Mr. Bleeding Edge, that's me! (Note: Unless you enjoy the Unix side of OS X and currently use cron to schedule tasks, this article won't be of much interest to you.)

Why now, after so long as a holdout? Primarily because I kept running into issues with cron tasks that needed to do things as "authorized me," such as mounting an encrypted disk image, or even just mounting a network share. Or my Mac would be asleep for a scheduled cron task, and it therefore wouldn't run. (launchd will queue any missed tasks to execute when the Mac reawakens.) Finally, my cron file was getting huge and unwieldy, and making simple changes was fraught with danger of breaking something.

So I dedicated a portion of a recent weekend to figuring out launchd, and migrating my cron tasks to this brave no-longer-at-all-new world. If you're still hanging on to cron, read on to see what I've learned about launchd—maybe it'll inspire you to move, too (or not).

This article is not a how-to on writing and working with launchd agents. Instead, it's a collection of knowledge that may help you (as it did me) get started with them. I've also included one real-world example of how I'm using an agent to accomplish a scheduled task—and that schedule involves watching a folder for changes, as you can do with Folder Action Scripts in Finder.

The basics

To work with launchd, you create a specially-structured XML file in your user's Library/LaunchAgents folder. One the file is created, you use launchctl to load (and unload and possibly start or stop) the file, and it will then run on whatever schedule and/or actions you specified in the XML file.

During my search and learning over that weekend, I found Nathan Grigg's Schedule jobs using launchd easy to understand, and using his demo file template got me started. He even discusses some third-party tools (which I'll get to later) that greatly ease the process of working with launchd.

As good as Nathan's page is, it lacks examples on how to schedule tasks on anything other than a number of seconds delay; to fill that hole, Alvin Alexander's post on StartInterval and StartCalendarInterval was very useful. I also found this page useful for its coverage of WatchPaths, which is a way to trigger an event based on changes in the content of a folder or folders.

My first launchd agent

I have a 4TB external drive that my wife keeps at her office, so we have an offsite backup. She brings it home once a week so I can update the backup. To remind her that it's the day to bring the drive home, I send her an email message on that day. I used to do this with a Calendar event and AppleScript, but had horrible luck with it—99% of the time, it wouldn't work at all.

This is what led me to originally investigate launch agents, as I hoped I could make the implementation more robust. After doing my research (using the above links), here's what my first Launch Agent wound up looking like:

The vast majority of the above is boilerplate; the only bits that I changed are the values for Label, Program, and StartCalendarInterval. Label needs to be unique for each cron task, and Program is what code the launch agent will execute. Launch agents can do more than just launch programs, but that's mainly how I'm using them.

In this case, the launch agent calls the binary code within an AppleScript app that sends the actual message. I've had this running for a few weeks now, and it's working perfectly.

Simplify the process

Once you have a template file, creating launchd agents isn't all that hard. But it is a manual process, and in particular, setting up recurring schedules can require a lot of typing. For example, consider a task that I want to run three times a day, at 10:20am, 4:20pm, and 11:20pm. As set up in cron, the schedule bit would look like this:

For launchd, it looks like this:

Ugh, that's a pain! Thankfully, there's a fix: third party apps. There are two GUI apps that I'm aware of, LaunchControl and Lingon. Both are $10, but I find LaunchControl to be more thorough and easier to use. For example, here's how that ugly three-times-daily schedule appears in LaunchControl:

Notice you can even enter cron style times, and LaunchControl will convert them to the new style for you—that's actually how I created this one, and any other complicated schedules. It's just much faster to enter a one-line cron schedule than to click Add over and over again.

LaunchControl also shows you an overview of all agents on your system, any errors when those agents tried to run, lets you load and unload jobs, and shows detailed explanations of each launchd function in a hideable side palette. There's even something of a debug mode, where you can redirect output to see why a task is failing.

If you're going to do much with launch agents, using one of these third party tools greatly eases the setup and maintenance of your agents.

Wrap up

So is cron gone for me for good? Not quite; there's still one task there: scheduled SuperDuper clones. That's because SuperDuper uses cron for its scheduled tasks, so it's out of my control. It'd be great if they switched to launchd at some point, but it's not a deal breaker.

Overall, I've been quite happy with the migration to launchd. It looks complex, but it's really no worse than cron, except for being so much more verbose. And with LaunchControl, that's not a problem. So if you're still cronning, give launchd a chance; you might, like I did, find it a worthwhile switch.

1 thought on “Goodbye cron task, hello launchd agent”

Comments are closed.