General documentation / cheat sheets for various languages and services has some great unofficial documentation for launchd.

Daemons vs. Agents

Daemons run apart from users, agents run specifically for users.

Read man launchctl for more.

Login Items

System PreferencesUsers & GroupsLogin Items

There aren’t many configuration options. Right clicking the listed items will bring up a context menu with a single “Show in Finder” option.

Login items can be added programmatically with osascript, e.g.:

osascript -e 'tell application "System Events" to make login item at end with properties {path:"/Applications/", hidden:false}'

Startup Items

More rarely used:


Kernel Extensions

Applications can also sneak themselves into your kernel extensions.

These are stored in the filesystem at /System/Library/Extensions.

List currently loaded extensions with:

sudo kextstat

Stop one like so:

sudo kextunload -b com.paceap.kext.pacesupport.snowleopard

While you can remove the files from /System/Library/Extensions, it appears that kextunload also causes the extension not to be loaded in future boots.


Based on my Server Fault answer.

To enable one-off logging, without using the filesystem:

sudo launchctl debug service-target --stdout --stderr

For that dev.localmon service, the following will pipe the process’s stdout and stderr into the shell the next time (and only the next time) the service starts:

sudo launchctl debug gui/$UID/dev.localmon --stdout --stderr

You must have already called launchctl load ~/Library/LaunchAgents/dev.localmon.plist before starting the debug session.

That command will hang with the open-and-ready TTYs. In another terminal, run:

launchctl start dev.localmon


launchctl does not have a reload command for reading changes to a config.plist file. Instead, you must unload and then load the plist file anew, e.g.:

launchctl unload ~/Library/LaunchAgents/local.ssh-add.plist
launchctl load $_

($_, like !$ refers to the last argument of the previous command)

Error interpretations

Defaults / environment

Tilde-expansion (~/Desktop) is a shell feature, and launchd does not implicitly run the program specified in ProgramArguments inside a shell, so you can’t use a tilde there, or in the other configuration values, to refer to the current home directory.

The working directory is always / unless you set it with WorkingDirectory, even for user agents (in ~/Library/LaunchAgents).

But if you start a bash shell, it will have access to the usual HOME and USER environment variables (among others).

The first string part of ProgramArguments does not have to be an absolute filename, though that is recommended, since it’s not clear what directories launchd will look in to find the executable a relative filename is referring to (like PATH in a shell). (Practically speaking, though, it seems that launchd’s “PATH” defaults to /usr/bin:/bin:/usr/sbin:/sbin, i.e., /etc/paths.)


local.ssh-add.plist, to load an SSH key at system startup:

Create a file at ~/Library/LaunchAgents/local.ssh-add.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "">
<plist version="1.0">

Then run:

launchctl load ~/Library/LaunchAgents/local.ssh-add.plist