Take a Python script, Rust binary, Go service, or shell one-liner and have it running as a launchd background service in under a minute. mklaunchd is the macOS equivalent of mkunit for systemd.
$ mklaunchd generate --label io.uradical.helloserver \ --binary /usr/local/bin/hello-server \ --write --yes ✓ Validation passed ✓ Created log directory: ~/Library/Logs/io.uradical.helloserver ✓ Plist written to ~/Library/LaunchAgents/io.uradical.helloserver.plist -- Next steps launchctl bootstrap gui/501 ~/Library/LaunchAgents/io.uradical.helloserver.plist launchctl print gui/501/io.uradical.helloserver launchctl list | grep io.uradical.helloserver To stop the service: launchctl bootout gui/501/io.uradical.helloserver
launchd is the right tool for personal automation on macOS — but its plist format is
verbose XML, the keys are easy to miss, the failure modes are silent, and
launchctl still bootstraps services into domain-scoped strings you have to
remember by hand. mklaunchd writes the file and prints the launchctl commands.
That's it. It does not wrap launchctl, replace it, or hide it.
Generates a valid plist, validates it before writing, creates the log directory, and prints the exact launchctl commands you need to load, inspect, reload, and unload the job.
It doesn't run launchctl on your behalf, abstract over it, or maintain a daemon of its own. Once the plist is on disk, you're back in standard Apple tooling.
Walk through prompts the first time, then drop the same flags into a script for the next twelve services.
Catches the silent failures — KeepAlive on a scheduled job, relative paths, UserName on a user agent — before launchd does.
Prints the exact bootstrap, print, list, and bootout commands tailored to the scope you picked. Nothing magic, nothing hidden.
User agent, system agent, or daemon — mklaunchd picks the right plist directory, the right launchd domain, and prepends sudo where you need it.
Drop an executable named mklaunchd-foo on PATH and it becomes a subcommand. Write them in any language; environment contract is documented.
One binary, one tap, one install. macOS-only by design — no cross-platform abstraction tax, no Linux compatibility shims.
Tap and install via Homebrew. macOS 13+.
brew tap uradical/mklaunchd brew install mklaunchd
Pass flags or run interactively. mklaunchd validates the plist and writes it to the right LaunchAgents directory.
mklaunchd generate \ --label io.uradical.helloserver \ --binary /usr/local/bin/hello-server \ --scope user-agent \ --write --yes
Copy the printed launchctl commands. Your service is now under standard macOS tooling — restart on reboot, log to ~/Library/Logs/<label>/, manageable from the same launchctl you'd use for any other plist.
launchctl bootstrap gui/$UID \ ~/Library/LaunchAgents/io.uradical.helloserver.plist
A run-at-load Launch Agent that restarts on crash, with a sane PATH and a stdout/stderr log directory. Hand-written on the left, generated on the right.
<?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>io.uradical.helloserver</string> <key>Program</key> <string>/usr/local/bin/hello-server</string> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <dict> <key>SuccessfulExit</key> <false/> </dict> <key>ThrottleInterval</key> <integer>10</integer> <key>EnvironmentVariables</key> <dict> <key>PATH</key> <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string> </dict> <key>StandardOutPath</key> <string>~/Library/Logs/io.uradical.helloserver/stdout.log</string> <key>StandardErrorPath</key> <string>~/Library/Logs/io.uradical.helloserver/stderr.log</string> </dict> </plist>
mklaunchd generate \ --label io.uradical.helloserver \ --binary /usr/local/bin/hello-server \ --write --yes # PATH, log paths, KeepAlive, ThrottleInterval, the # XML scaffolding, the log directory, and the # launchctl bootstrap commands all come for free. # Want to see it without writing? Pipe to a file: mklaunchd generate --label ... --binary ... --stdout > out.plist # Want it interactive? Just run: mklaunchd generate
Requires macOS 13+. Source on GitHub, MIT licensed.
Pre-1.0. In active development — CLI flags and conventions may change before v1.
brew tap uradical/mklaunchd brew install mklaunchd mklaunchd generate
Until v1.0: the Homebrew tap is not yet published. Build from source instead:
git clone https://github.com/uradical/mklaunchd cd mklaunchd make install