Replacing Cron Jobs With Launchd on OS X

Photo of Greg
Photo by Alan Cleaver - http://flic.kr/p/54buYe

Several basic setup tasks on OS X Server — especially those related to maintaining backups — can be made easier via launchd, OS X’s replacement for cron.

On Mac OS X, the venerable cron has been deprecated since 10.4, so if you’d like to schedule jobs to run at particular intervals on your OS X Server, the preferred approach is to use launchd instead. Scheduling jobs with launchd is a little more involved than with , but it’s not too onerous.

In place of a single crontab scheduling actions for each user on the server, launchd draws on a collection of .plist files stored in various locations depending on whether the actions are to be scheduled for individual users or for the server as a whole. Although it might seem unnecessarily complicated at first glance, it’s the splitting up of each task specification into an individual, structured plist which brings a great deal more flexibility to the launchd system than was possible with single lines in a crontab.

To schedule a task with launchd so it will run regardless of whether a specific individual user is logged in, begin by creating a .plist file and dropping it into /Library/LaunchDaemons/. Individual jobs can be enabled or disabled, respectively, with the following two lines:

sudo launchctl load /Library/LaunchDaemons/example.plist

sudo launchctl unload /Library/LaunchDaemons/example.plist

The structure of a basic launchd plist looks like this, with two optional extras that are particularly useful when scheduling for a server, an array for StartCalendarInterval and a specific UserName key:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Label</key>
	<string>launchd_example</string>
	<key>ProgramArguments</key>
	<array>
		<string>touch</string>
		<string>/Users/myusername/example.txt</string>
		<string>sleep</string>
		<string>10</string>
	</array>
	<key>QueueDirectories</key>
	<array/>
	<key>StartCalendarInterval</key>
	<array>
		<dict>
			<key>Hour</key>
			<integer>2</integer>
			<key>Minute</key>
			<integer>0</integer>
			<key>Weekday</key>
			<integer>2</integer>
		</dict>
		<dict>
			<key>Hour</key>
			<integer>2</integer>
			<key>Minute</key>
			<integer>0</integer>
			<key>Weekday</key>
			<integer>5</integer>
		</dict>
	</array>
	<key>UserName</key>
	<string>myusername</string>
	<key>WatchPaths</key>
	<array/>
</dict>
</plist>

This example, which would be saved as launchd_example.plist, will touch /Users/myusername/example.txt every Tuesday and Friday at 2:00 a.m. and then wait for 10 seconds before finishing. There are several other ways of scheduling described in Apple’s documentation, but I’ve chosen this particular method because it corresponds to a commonly used pattern for cron jobs, where you may be accustomed to configuring a cron job to run on certain days of the week at certain times.

In addition, this example demonstrates the use of the ‘UserName’ key. On a server, we almost always want to specify system-level jobs rather than user-level jobs, since the latter run only when a given user is logged in. But system-level jobs normally run as root. If we don’t need or want that for whatever reason, we can simply specify the name of the user under which the job should be run. The job will still run regardless of whether that user is logged in, but it will run as that user. Generally speaking, it is preferable to take jobs which do not need root access and run them as a user other than root. For example, if you’re expiring site-specific cache files that have been generated by code running as Apache on a given site, these jobs can be run as the ‘_www’ user, thereby avoiding any risks inherent in running them as root.

It is worth reading Apple’s documentation about launchd, by the way, as it includes some important little nuggets, such as:

Important: If your daemon shuts down too quickly after being launched, launchd may think it has crashed. Daemons that continue this behavior may be suspended and not launched again when future requests arrive. To avoid this behavior, do not shut down for at least 10 seconds after launch.

(Thus the 10-second wait included in the above example.)

Finally, note that you can edit plist files either manually with a plain text editor, or with one of several editors able to speak plist, including Xcode. Lingon was a popular graphical front-end specifically intended for editing launch agents and launch daemons, and in fact the original (free) version of 2 of Lingon, before it moved to the Mac App Store, is still entirely usable for exactly this purpose. Unfortunately, the current paid version is severely limited due to restrictions on apps allowed to appear in the Mac App Store and so can no longer be used to edit or save system-level jobs. (It’s a bit fiddly, but the editing function can still be used: just copy and save the resulting text yourself into the correct position and load it yourself from the command line.)

All material on this site is carefully reviewed, but its accuracy cannot be guaranteed, and some suggestions offered here might just be silly ideas. For best results, please do your own checking and verifying. This specific article was last reviewed or updated by Greg on .

This site is provided for informational and entertainment purposes only. It is not intended to provide advice of any kind.