Managing services on macos with launchctl

Reading time: about 2 minutes

Managing services on MacOS with launchctl

A lot of modern tools work hard to mask the service management system which has been built into MacOS. After researching it, you might want to mask and shim it too. Brew dedicates a few hundred lines of ruby to interacting with launchd.

Resources

Patterns

A service definition is written in MacOS plist format and saved as a .plist. They can be saved anywhere but by convention they’re located in one of two places:

System launched services are started at boot time, user-launched services are started when that user logs in.

The distinction between Agent and Daemon is given in man 8 launchd:

In the launchd lexicon, a daemon is, by definition, a system-wide service of which there is one instance for all clients. An agent is a service that runs on a per-user basis.

Examples

Installed in my ~/Library/LaunchAgents right now:

And installed in my /Library/LaunchDaemons:

Starting, stopping, and inspecting

Deprecated subcommands

The manual cites these patterns as “legacy” though they continue to work.

$ launchctl load ~/Library/LaunchAgents/com.whatever.whatever.plist
~/Library/LaunchAgents/com.whatever.whatever.plist operation in progress

$ launchctl unload ~/Library/LaunchAgents/com.whatever.whatever.plist
~/Library/LaunchAgents/com.whatever.whatever.plist operation in progress

$ launchctl list
PID	Status	Label
-	0	com.apple.whatever

List services

There isn’t a great way to do this. The deprecated list subcommand is barely functional. brew service status uses launchctl list.

The columnar data isn’t transparent, but it is helpful. From man launchctl:

The first column displays the PID of the job if it is running. The second column displays the last exit status of the job. If the number in this column is negative, it represents the negative of the signal which stopped the job.

Start a service

Given a service isn’t running:

> launchctl list | grep redis
-	-9	homebrew.mxcl.redis

It can be started with kickstart:

> launchctl kickstart -p gui/501/homebrew.mxcl.redis
service spawned with pid: 91223

-p Upon success, print the PID of the new process […]

Stop a running service

Given a running service:

> launchctl list | grep redis
91178	-3	homebrew.mxcl.redis

Send it a signal:

> launchctl kill SIGKILL gui/501/homebrew.mxcl.redis

And check that it has been killed:

> launchctl list | grep redis
-	-9	homebrew.mxcl.redis

Date: 2021-Nov-11
Tags: macos launchd launchctl homebrew
Previous: Installing Crystal+Lucky on M1 Mac
Next: Organizing files in a terraform codebase