Skip to content
C.W.K.
Stream
Lesson 08 of 10 · published

System Services (brew services, launchctl)

~10 min · services, launchctl, brew

Level 0Window Tourist
0 XP0/95 lessons0/14 achievements
0/100 XP to next level100 XP to go0% complete

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@16

Behind 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.svc

LaunchAgent 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/.

A user-level development service usually belongs in a LaunchAgent because it runs after login without root. Use a LaunchDaemon only when the service genuinely needs a system-wide, 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.

The service declaration owns lifecycle

A launchd plist must make program arguments, working directory, environment, logs, restart behavior, and stop semantics explicit. After a declaration changes, use the appropriate bootout/bootstrap or kickstart workflow for its domain and confirm the loaded job, PID, logs, and real health rather than starting a duplicate process by hand.

Code

brew services workflow·bash
brew install redis
brew services start redis
brew services list
redis-cli ping     # PONG
brew services stop redis

External links

Exercise

If you have postgres or redis installed via brew, run brew services list. Start one, run a tiny client check (redis-cli ping, psql -l), stop it. Then launchctl list | grep -v com.apple | head to see launchd entries.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.