macOS launchd — SKILL.md
Raw skill file that agents receive when using this skill
---
name: "macOS launchd"
description: "Skill for macOS launchd — auto-generated from documentation"
version: "1.0.0"
author: "skynet"
category: "ops"
agents: ["claude-code", "codex", "gemini"]
tags: ["launchd", "ops", "auto-generated"]
---
# macOS launchd
---
name: macOS launchd
description: Use this skill when managing system services, background processes, or scheduled tasks on macOS. Essential for automating scripts, managing daemons, and controlling system startup behavior.
metadata:
author: skynet
version: 1.0.0
category: ops
---
# macOS launchd Management
## Overview
launchd is macOS's unified service management framework that handles daemons, agents, and scheduled tasks. It replaces cron, xinetd, and other legacy process managers.
## Key Concepts
- **LaunchDaemons**: System-wide services (run as root)
- **LaunchAgents**: Per-user services (run as user)
- **Property Lists (plist)**: XML configuration files defining jobs
## Location Structure
```
/System/Library/LaunchDaemons/ # System daemons (Apple)
/System/Library/LaunchAgents/ # System agents (Apple)
/Library/LaunchDaemons/ # Custom system daemons (root)
/Library/LaunchAgents/ # Custom system agents (all users)
~/Library/LaunchAgents/ # User-specific agents
```
## Essential Commands
### Service Management
```bash
# Load service
sudo launchctl load /Library/LaunchDaemons/com.example.service.plist
# Unload service
sudo launchctl unload /Library/LaunchDaemons/com.example.service.plist
# Start service immediately
sudo launchctl start com.example.service
# Stop running service
sudo launchctl stop com.example.service
# List all loaded services
launchctl list
# Check specific service status
launchctl list | grep com.example.service
# Get service info
launchctl print system/com.example.service
```
### Bootstrap Management (macOS 10.11+)
```bash
# Load into system domain
sudo launchctl bootstrap system /Library/LaunchDaemons/com.example.service.plist
# Load into user domain
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.example.agent.plist
# Remove from domain
sudo launchctl bootout system/com.example.service
# Enable service
sudo launchctl enable system/com.example.service
# Disable service
sudo launchctl disable system/com.example.service
```
## Creating Basic plist Files
### Simple Daemon Template
```xml
<?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.example.mydaemon</string>
<key>Program</key>
<string>/usr/local/bin/mydaemon</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/var/log/mydaemon.log</string>
<key>StandardErrorPath</key>
<string>/var/log/mydaemon.error.log</string>
</dict>
</plist>
```
### Scheduled Task (Cron Replacement)
```xml
<?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.example.backup</string>
<key>Program</key>
<string>/usr/local/bin/backup.sh</string>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>2</integer>
<key>Minute</key>
<integer>30</integer>
</dict>
<key>StandardOutPath</key>
<string>/var/log/backup.log</string>
</dict>
</plist>
```
### Web Service with Environment Variables
```xml
<?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.example.webservice</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/node</string>
<string>/opt/myapp/server.js</string>
</array>
<key>WorkingDirectory</key>
<string>/opt/myapp</string>
<key>EnvironmentVariables</key>
<dict>
<key>NODE_ENV</key>
<string>production</string>
<key>PORT</key>
<string>3000</string>
</dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>UserName</key>
<string>_www</string>
<key>GroupName</key>
<string>_www</string>
</dict>
</plist>
```
## Common Configuration Keys
### Essential Keys
- `Label`: Unique identifier (reverse domain notation)
- `Program`: Path to executable
- `ProgramArguments`: Array of program and arguments
- `RunAtLoad`: Start when loaded
- `KeepAlive`: Restart if crashes
### Scheduling Keys
- `StartCalendarInterval`: Specific times/dates
- `StartInterval`: Regular intervals (seconds)
- `WatchPaths`: Monitor file/directory changes
### Resource Keys
- `WorkingDirectory`: Set working directory
- `UserName`/`GroupName`: Run as specific user/group
- `EnvironmentVariables`: Set environment
### Logging Keys
- `StandardOutPath`: Redirect stdout
- `StandardErrorPath`: Redirect stderr
- `StandardInPath`: Redirect stdin
## Decision Tree: Service Type Selection
```
Need to run a service?
├── System-wide (all users)?
│ ├── Background service → LaunchDaemon (/Library/LaunchDaemons/)
│ └── User interaction needed → LaunchAgent (/Library/LaunchAgents/)
└── Single user only?
└── User-specific service → LaunchAgent (~/Library/LaunchAgents/)
When to start?
├── System boot → RunAtLoad + LaunchDaemon
├── User login → RunAtLoad + LaunchAgent
├── Scheduled time → StartCalendarInterval
├── File changes → WatchPaths
└── On demand → omit RunAtLoad
```
## Workflow: Creating and Installing a Service
```bash
# 1. Create plist file
sudo vim /Library/LaunchDaemons/com.example.myservice.plist
# 2. Set proper permissions
sudo chown root:wheel /Library/LaunchDaemons/com.example.myservice.plist
sudo chmod 644 /Library/LaunchDaemons/com.example.myservice.plist
# 3. Validate plist syntax
plutil -lint /Library/LaunchDaemons/com.example.myservice.plist
# 4. Load service
sudo launchctl bootstrap system /Library/LaunchDaemons/com.example.myservice.plist
# 5. Enable service
sudo launchctl enable system/com.example.myservice
# 6. Start service
sudo launchctl kickstart system/com.example.myservice
# 7. Verify status
sudo launchctl print system/com.example.myservice
```
## Debugging and Monitoring
### Check Service Status
```bash
# List all services with status
launchctl list | grep -E "(PID|Label)"
# Detailed service info
launchctl print system/com.example.service
# Check if service is enabled
launchctl print-disabled system | grep com.example.service
# View service logs
tail -f /var/log/system.log | grep com.example.service
```
### Console Logs
```bash
# Real-time system logs
log stream --predicate 'subsystem == "com.apple.launchd"'
# Service-specific logs
log show --predicate 'subsystem == "com.example.service"' --last 1h
```
## Troubleshooting
### Common Error: "Operation not permitted"
```
Error: Bootstrap failed: 5: Input/output error
```
**Fix**: Check plist ownership and permissions
```bash
sudo chown root:wheel /Library/LaunchDaemons/com.example.service.plist
sudo chmod 644 /Library/LaunchDaemons/com.example.service.plist
```
### Common Error: "Could not find specified service"
```
Error: Could not find service "com.example.service" in domain for system
```
**Fix**: Service not loaded or wrong domain
```bash
# List what's actually loaded
sudo launchctl print system | grep com.example
# Bootstrap if missing
sudo launchctl bootstrap system /path/to/service.plist
```
### Common Error: Service keeps crashing
**Check**: Console.app → System Reports → LaunchDaemons
**Fix**: Add debugging to plist
```xml
<key>StandardOutPath</key>
<string>/tmp/myservice.log</string>
<key>StandardErrorPath</key>
<string>/tmp/myservice.error.log</string>
```
### Common Error: "Invalid property list"
**Check**: Syntax validation
```bash
plutil -lint /path/to/service.plist
xmllint --format /path/to/service.plist
```
### Service won't start at boot
**Fix**: Verify RunAtLoad and check system integrity
```bash
# Check if disabled
launchctl print-disabled system
# Re-enable if needed
sudo launchctl enable system/com.example.service
```
## Best Practices
### Security
- Use specific user accounts, not root when possible
- Set minimal file permissions (644 for plists)
- Validate all paths in plist files
### Reliability
- Always include StandardOutPath and StandardErrorPath
- Use KeepAlive judiciously (can mask real problems)
- Test services manually before installing
### Maintenance
- Use consistent naming (reverse domain notation)
- Document environment requirements
- Version control your plist files
- Monitor logs regularly
curl -s https://skills.skynet.ceo/api/skills/launchd/skill.md