macOS Ops: launchd Jobs debuggen und Homebrew Services sauber betreiben
Wer macOS im professionellen Umfeld administriert, kommt an launchd und Homebrew nicht vorbei. Doch wie verhalten sich beide zusammen, wenn es um persistente Services geht? Dieser Beitrag bringt Licht ins Dunkel und zeigt praxistaugliche Konfigurationen.
Zwei Welten: LaunchAgents vs. LaunchDaemons
- LaunchAgents: laufen im Kontext eines Users (GUI-Session), liegen in
~/Library/LaunchAgents - LaunchDaemons: systemweit, laufen als root, liegen in
/Library/LaunchDaemons
Das ist nicht Kosmetik: Es entscheidet über Rechte, Environment und Zugriff auf Keychain/GUI.
Der Klassiker: PATH ist nicht dein PATH
launchd startet Jobs mit einem sehr kleinen Environment. Wenn euer Script brew, python, jq usw. braucht, gebt die Pfade explizit an oder setzt PATH.
Besser: ProgramArguments statt Program
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- file: com.example.cleanup.plist -->
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.example.cleanup</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>/usr/local/scripts/cleanup.sh</string>
</array>
<key>StartInterval</key>
<integer>3600</integer>
<key>StandardOutPath</key>
<string>/var/log/com.example.cleanup.out</string>
<key>StandardErrorPath</key>
<string>/var/log/com.example.cleanup.err</string>
</dict>
</plist>
Damit seht ihr auch, ob das Script überhaupt startet.
launchd Debug-Workflow
1) Status prüfen
1
2
3
4
5
# Agent (User)
launchctl print gui/$(id -u)/com.example.cleanup
# Daemon (System)
sudo launchctl print system/com.example.cleanup
2) Job (neu) laden
1
2
3
4
5
# Agent
aunchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.example.cleanup.plist 2>/dev/null || true
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.example.cleanup.plist
launchctl enable gui/$(id -u)/com.example.cleanup
launchctl kickstart -k gui/$(id -u)/com.example.cleanup
Wenn bootstrap meckert: Plist prüfen (plutil -lint …) und Pfade.
3) Logs ansehen
Auf neueren macOS Versionen ist Unified Logging der Standard.
1
log show --style syslog --last 10m --predicate 'process == "launchd"'
Wenn ihr StandardOutPath nutzt, könnt ihr zusätzlich klassisch tailen.
Homebrew Services: Komfort ja, aber bewusst
brew services ist praktisch, weil es Plists generiert und managed. Aber: ihr solltet wissen, ob es als User-Agent oder als System-Daemon läuft.
1
2
brew services list
brew services info <formula>
Empfehlung: Services dokumentieren
Für produktionsnahe Macs (Build-Agents, Jump-Hosts, Lab-Rechner):
- welche Services laufen?
- warum?
- unter welchem User?
- wo landen Logs?
Takeaways
- launchd hat ein minimales Environment: Pfade explizit setzen.
ProgramArgumentsist robuster alsProgram.- Debugging beginnt mit
launchctl printund endet mit brauchbaren Logs. brew servicesist bequem, aber ihr müsst User vs. System unterscheiden.- Dokumentiert laufende Services (Owner/Zweck/Logs), sonst wird’s Wildwuchs.
