Skip to content

Service Management with launchd

pkgdemon edited this page Aug 21, 2026 · 2 revisions

NextBSD has no rc.conf and no rc.d scripts. launchd is PID 1, and every service is described by a property list.

If you are coming from FreeBSD, the translation is:

FreeBSD NextBSD
sshd_enable="YES" in rc.conf com.openssh.sshd.plist present and loaded
service sshd start launchctl load /System/Library/LaunchDaemons/com.openssh.sshd.plist
service sshd stop launchctl unload …
service -e launchctl list
/usr/local/etc/rc.d/foo /Local/Library/LaunchDaemons/foo.plist

Where job plists live

launchd scans two directories, in this order:

Directory For
/System/Library/LaunchDaemons/ OS-provided daemons. Treat as immutable.
/Local/Library/LaunchDaemons/ Anything you or a package installs on this machine.

Put your own services in /Local/Library/LaunchDaemons/. See Filesystem Layout for why the split exists.

What ships by default

Plist Service
com.apple.syslogd.plist Apple System Logger
com.apple.aslmanager.plist Log rotation and expiry
com.apple.notifyd.plist Named-event notification bus
com.apple.configd.plist SCDynamicStore, the system config store
com.apple.IPConfiguration.plist ipconfigd — DHCP client
com.apple.mDNSResponder.plist Bonjour / DNS-SD
com.apple.hostnamed.plist Hostname management
com.apple.kextd.plist Kernel extension daemon
com.apple.DiskArbitration.plist Disk arbitration
com.openssh.sshd.plist OpenSSH server
org.nextbsd.wland.plist Wireless daemon
org.nextbsd.getty.console.plist Console getty
org.nextbsd.getty.ttyv0.plist Virtual terminal getty

Everyday commands

launchctl list                          # everything launchd knows about
launchctl list com.apple.syslogd        # one job's plist, as loaded

launchctl load   -w /Local/Library/LaunchDaemons/com.example.myservice.plist
launchctl unload -w /Local/Library/LaunchDaemons/com.example.myservice.plist

The -w flag persists the change across reboots by writing the job's enabled/disabled state, rather than only affecting the running system. Without it, a load lasts until the next boot.

Writing a job plist

A minimal service that runs one program and is restarted if it exits:

<?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.myservice</string>

    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/bin/myservice</string>
        <string>--foreground</string>
    </array>

    <key>RunAtLoad</key>
    <true/>

    <key>KeepAlive</key>
    <true/>

    <key>StandardOutPath</key>
    <string>/var/log/myservice.log</string>
    <key>StandardErrorPath</key>
    <string>/var/log/myservice.log</string>
</dict>
</plist>

Save it as /Local/Library/LaunchDaemons/com.example.myservice.plist, then:

launchctl load -w /Local/Library/LaunchDaemons/com.example.myservice.plist
launchctl list com.example.myservice

man launchd.plist documents every available key.

The one mistake everyone makes

Your program must not fork into the background. launchd tracks the process it started; if that process forks and the parent exits, launchd sees the service die and — with KeepAlive set — restarts it, forever.

Almost every daemon has a flag for this: -f, --foreground, -D, --nodaemon. Use it. If you see a service respawning in a tight loop, this is nearly always why.

Permissions

launchd refuses to load a daemon plist that is group- or world-writable. The correct mode is 644, owned root:wheel:

chown root:wheel /Local/Library/LaunchDaemons/com.example.myservice.plist
chmod 644 /Local/Library/LaunchDaemons/com.example.myservice.plist

Files created by root already come out this way under the default umask of 022, so this is usually only needed after copying from elsewhere.

Debugging a service that will not start

launchctl list com.example.myservice    # is it even loaded?
syslog -k Sender com.example.myservice  # what did it say before dying?
syslog -F bsd | tail -50                # recent system log

Check, in order: the plist parses as valid XML; the Label matches the filename; ProgramArguments[0] is an absolute path to something executable; and the program stays in the foreground.

See also

Clone this wiki locally