Long-running processes that boot with the Mac
Web servers, databases, message brokers — they need to start when the machine boots and restart on crash. macOS uses launchd to manage them. Two convenient front ends sit on top:
brew services— for tools installed via Homebrew.launchctl— the raw command for any plist.
brew services
brew install postgresql@16
brew services start postgresql@16
brew services list
brew services stop postgresql@16
brew services restart postgresql@16
# Run without auto-start at login
brew services run postgresql@16Behind the scenes brew writes a LaunchAgent plist into ~/Library/LaunchAgents/ and loads it.
launchctl directly
# See what's loaded for your user
launchctl list | grep -v com.apple
# Bootstrap (load) a plist
launchctl bootstrap gui/$UID ~/Library/LaunchAgents/com.me.svc.plist
# Bootout (unload)
launchctl bootout gui/$UID/com.me.svc
# Restart cleanly
launchctl kickstart -k gui/$UID/com.me.svcLaunchAgent vs LaunchDaemon
- LaunchAgent — runs as your user when you log in. Lives under
~/Library/LaunchAgents/or/Library/LaunchAgents/. - LaunchDaemon — runs as root at boot, before any user logs in. Lives under
/Library/LaunchDaemons/.
For Pippa's pippa serve, a LaunchAgent is enough — it runs as you, after login. Don't reach for LaunchDaemons unless you need root and a no-user-required start.
Logs
The plist's StandardOutPath and StandardErrorPath tell launchd where to send output. Without them, output is dropped. Always set them when debugging.