I self host a lot of stuff — these days, mostly weird little utility scripts and toys that run in the background, but also some web apps like plex, calibre, and a suite of irc things. For a long time I ran such things on a VPS, but being incredibly cheap, and hardly ever leaving my house for realsies, during the height of the pandemic I brought everything on to an aged mac mini I keep on a shelf behind some books.
I tried using a few applications like Lingon X to manage these services, but was supremely unimpressed (read as “couldn’t figure out”) their configuration options. But, there’s an inbuilt option if you aren’t afraid of creating and fiddling with plist files.
launchd is Apple’s init system. It is responsible for starting, stopping, and managing background processes. It lives and dies by plist files. Contemporary(ish) macOS has two confusingly named library folders,
- Create a plist file in one of the following places:
- ~/Library/LaunchAgents/ - user-level services (runs when a specific user logs in)
- /Library/LaunchAgents/ - system-wide services (runs when any user logs in)
- /Library/LaunchDaemons/ - system-wide services (runs at boot before any user logs in)
- Use this template to create a plist file like…
where username is a valid username, and someservice is the name of the service you’d like to run.
<?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>com.username.someservice</string> <key>ProgramArguments</key> <array> <string>/absolute/path/to/the/program</string> <string>arg1</string> <string>arg2</string> </array> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <true/> <key>StandardOutPath</key> <string>/tmp/someservice.stdout.log</string> <key>StandardErrorPath</key> <string>/tmp/someservice.stderr.log</string> </dict> </plist>- Load the service:
And a quick and dirty reference to available keys in launchd plist files:
- Label: A unique identifier for the service
- ProgramArguments: The executable path and any arguments
- RunAtLoad: Whether to start the service when the plist is loaded
- KeepAlive: Whether to restart the service if it crashes
- WorkingDirectory: The directory the program runs from
- StandardOutPath and StandardErrorPath: Where to send stdout and stderr
- EnvironmentVariables: Environment variables for the program
- StartInterval: Run the service periodically (in seconds)
- StartCalendarInterval: Run the service at specific times
Here’s an example of how I run the ergo irc server:
<?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>com.eli.ergo</string> <key>ProgramArguments</key> <array> <string>/Users/eli/ergo/ergo</string> <string>run</string> </array> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <true/> <key>WorkingDirectory</key> <string>/Users/eli/ergo</string> <key>StandardOutPath</key> <string>/tmp/ergo.stdout.log</string> <key>StandardErrorPath</key> <string>/tmp/ergo.stderr.log</string> </dict> </plist>And here’s an example for a Node.js application:
<?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>com.eli.thelounge</string> <key>ProgramArguments</key> <array> <string>/usr/local/bin/thelounge</string> <string>start</string> <string>port=9003</string> </array> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <true/> <key>StandardOutPath</key> <string>/tmp/thelounge.stdout.log</string> <key>StandardErrorPath</key> <string>/tmp/thelounge.stderr.log</string> <key>EnvironmentVariables</key> <dict> <key>PATH</key> <string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string> </dict> </dict> </plist>And while I know I could also use homebrew services to accomplish this, I found that system opaque, and dependent so I poo-pooed it for my own needs.
A solid reference (maybe the only one?) for all things launchd.
Published May 10, 2025
Tags.png)


