Jack of all tasks: launchd, and how to run periodic tasks

OS X may seem inordinately complicated, to the point where it all becomes a meaningless blur, but it really boils down to two major components: the kernel, and launchd. The kernel is the heart, vital to everything, and its failure is catastrophe. Kernel extensions (KEXTs) extend its functionality to operate hardware beyond the essential core.

The kernel cannot, though, do much else, for which it looks to launchd, the first task to run outside the kernel and always with the process ID of 1. launchd then gets everything else loaded up and running, from services which have to be started each time that OS X starts up, to those routine housekeeping functions which run in the middle of the night. If the kernel is the heart, then surely launchd is its brain.

If you want to get a quick idea of how much launchd is responsible for, you can use its control and admin tool launchctl to list all the tasks which it is currently managing, with the command
launchctl print-cache
which will generate a huge list of all sorts of things that you didn’t know that your Mac was up to.

launchd is a relatively recent innovation in OS X: it was not there in the first release, not appearing until OS X 10.4 (Tiger). Designed and written by Dave Zarzycki, its beauty lies in bringing together, into a single service, all the things that several other services did. The most prominent of those is the standard Unix service for running scheduled commands, cron. Although OS X still has cron and can still run timed events with it, it does so thanks to launchd.


If you wanted to back your Home folder up to a networked file server at 2300 every weeknight, for example, in traditional Unix you would write an entry in your crontab, a table which acts as the timetable for such events. The diehard Unix wizard can still do that in OS X, but Apple discourages you from doing so. At some stages during the introduction of launchd, the cron service, inevitably known as crond, became extremely unreliable, forcing users to migrate to launchd.

Chances are that nothing on your Mac now uses the old, deprecated cron system with crontabs. You can check by looking for its crontabs, located in /etc/crontab or /usr/lib/cron/tabs. Because the latter are protected (they are user-specific), the best way to check on these is with the commands
ls -la /etc/crontabs
which will almost certainly return No such file or directory
sudo ls -la /usr/lib/cron/tabs/username
where username is your short user name, and should return an empty folder.

Without crontabs, the only way that your Mac is going to run scheduled commands is under launchd.


Instead of crontab entries, launchd relies on XML property lists (.plist files) which are placed in standard locations:

  • ~/Library/LaunchAgents for user agents provided by the user,
  • /Library/LaunchAgents for user agents provided by the administrator,
  • /Library/LaunchDaemons for system-wide daemons provided by the administrator,
  • /System/Library/LaunchAgents for user agents provided by Apple,
  • /System/Library/LaunchDaemons for system-wide daemons provided by Apple.

It is easy to remember which is which: if they’re in either of the folders in /System/Library/, they should now be part of OS X, and protected by SIP, so that you cannot tamper with them. If they’re in either of the folders in /Library, then they will run for all users, and typically support apps, hardware, and other services which are accessible to all users. If they’re in ~/Library/LaunchAgents they are specific to that user alone.

The differences between daemons and agents can appear subtle, but there is one which is decisive: daemons cannot display any sort of user interface, or interact directly with your login session; if the service or action needs an interface or interaction, then it should go into one of the LaunchAgents folders.

Adding and controlling agents and daemons

There are three ways that you can create and add a new agent/daemon, or modify the settings of an existing one. If you want to do this on a temporary basis, and enjoy using the command line, then the shell command launchctl provides a complete set of commands for doing that. And the best of luck to you – it is not at all easy.

In most cases, you will want any changes to persist into the future. In that case, you will need to create a new XML property list and install it into the appropriate folder, or edit an existing one. Although this is not as tough as using launchctl, you will need to be comfortable editing property lists, and study Apple’s documentation for these particular files.

By far the best way of adding to or tweaking the property lists is using Peter Borg’s excellent Lingon X. This comes in two different versions: that offered in the App Store has limited functionality because of the constraints placed on App Store apps. It is perfectly suitable for creating your own periodic tasks, but if you want full access to its power you will need to purchase the unlimited version of Lingon X from here.


A service which runs every day might be set up like this Adobe service. Each time that my Mac starts up, and every twenty-four hours afterwards, this will automatically run the background service named AGSService. You could configure a similar service to synchronise the contents of a working documents folder with that on a file server, for example, in the same way. Instead of using a repeat interval like this, you could set a fixed time each day, perhaps.


The launchctl/launchd system allows more advanced options too, which you can also edit easily using Lingon. Most common among these are the bottom two, which set the paths for normal and error output.

So long as you are working with a small number of daemons/agents, launchd is not that complex, and certainly not difficult to use. It can get messier when you need to know about other tasks. One example of this is when your Mac starts crashing or freezing at 0200 every morning, but the logs provide insufficient information to identify what has caused the problem – something that I have recently experienced.

Trying to use launchctl to list all tasks is futile: for a start you’d never find that needle in its haystack, and in any case if the service is only run at 0200, you won’t find it there now. A better solution is to browse those property list files, either in the Finder or using Lingon, until you identify which is configured to run then. You could then disable it, and see whether that made the problem vanish too.

Further information

launchd, launchctl, cron, and crontabs are fully documented in their man files. The best overall guide to launchd is at launchd.info, which explains how to make your own property list files and much more.