macOS
The binary under launchd, and why a laptop is a poor host.
View as MarkdownmacOS runs the same static binary as Linux. The two things that differ are Gatekeeper, which
will refuse a downloaded binary if you fetch it the wrong way, and launchd, which gives a
daemon a minimal PATH and so cannot see anything Homebrew installed.
Download and verify
VERSION=v0.1.0
ARCH=$(uname -m); case "$ARCH" in x86_64) ARCH=amd64 ;; esac
curl -fsSLO "https://github.com/savedhq/local-worker/releases/download/${VERSION}/local-worker-darwin-${ARCH}"
curl -fsSLO "https://github.com/savedhq/local-worker/releases/download/${VERSION}/SHA256SUMS"
grep "local-worker-darwin-${ARCH}\$" SHA256SUMS | shasum -a 256 -c -uname -m already prints arm64 on Apple silicon, which is the name the release uses. Only
Intel needs the rename.
sudo install -m 0755 "local-worker-darwin-${ARCH}" /usr/local/bin/local-workerDownload with curl, not a browser. A file fetched by Safari or Chrome carries a
quarantine attribute and Gatekeeper will refuse to execute it. If you hit it anyway, clear
it with xattr -d com.apple.quarantine ./local-worker.
Put the config somewhere
sudo install -d -m 0700 /usr/local/etc/saved
sudo install -m 0600 config.yaml /usr/local/etc/saved/config.yamlA LaunchDaemon runs as root unless you tell it otherwise, so root-owned 0600 is the
correct mode. If you would rather it ran as an existing account, add a UserName key to the
plist below and chown the config to match.
Point at the tools explicitly
This is the one thing that catches everyone. launchd starts a daemon with a minimal PATH
of /usr/bin:/bin:/usr/sbin:/sbin. Homebrew installs to /opt/homebrew/bin on Apple silicon
and /usr/local/bin on Intel, so a worker that finds pg_dump perfectly well in your shell
will log external tool not found under launchd.
Give the absolute paths in config.yaml rather than hoping:
tools:
pg_dump: /opt/homebrew/bin/pg_dump
mysqldump: /opt/homebrew/bin/mysqldump
curl: /usr/bin/curlwhich pg_dump in a normal shell tells you what to write.
Run it once by hand
cd /usr/local/etc/saved && sudo /usr/local/bin/local-workerConfirm the external tool resolved lines name the paths you expect and that backups=
matches what this worker should serve, then stop it with Ctrl-C. See
what healthy looks like.
The daemon
<?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>sh.saved.worker</string>
<key>ProgramArguments</key> <array><string>/usr/local/bin/local-worker</string></array>
<key>WorkingDirectory</key> <string>/usr/local/etc/saved</string>
<key>RunAtLoad</key> <true/>
<key>KeepAlive</key> <true/>
<key>StandardOutPath</key> <string>/var/log/saved-worker.log</string>
<key>StandardErrorPath</key> <string>/var/log/saved-worker.log</string>
</dict>
</plist>WorkingDirectory is not optional. It is the only way the worker finds its config.
sudo chown root:wheel /Library/LaunchDaemons/sh.saved.worker.plist
sudo chmod 0644 /Library/LaunchDaemons/sh.saved.worker.plist
sudo launchctl bootstrap system /Library/LaunchDaemons/sh.saved.worker.plist
sudo launchctl print system/sh.saved.worker
tail -f /var/log/saved-worker.logbootstrap and bootout replace the deprecated load and unload. To restart after
editing the config:
sudo launchctl kickstart -k system/sh.saved.workerTo remove it entirely:
sudo launchctl bootout system/sh.saved.workerLaunchAgent, and why probably not
A LaunchAgent in ~/Library/LaunchAgents is the same plist bootstrapped into
gui/$(id -u) instead of system. It is the right choice for a worker you are testing
against a development database, and the wrong one for anything on a schedule.
| LaunchDaemon | LaunchAgent | |
|---|---|---|
| Runs | At boot, no login needed | Only while you are logged in |
| Survives logout | Yes | No |
| Runs as | root, or UserName | You |
A sleeping Mac runs nothing. Neither kind of job fires while the machine is asleep, and a
closed laptop lid is asleep. If this host must keep to a nightly schedule, it needs to be a
machine that stays awake: check pmset -g and set sleep 0 on a Mac that is meant to be a
server. A laptop is a poor host for a nightly backup.