How to run a Homebrew service as yourself
Petros Amoiridis
On a managed Device, brew services start runs a service as the workbrew account, with its home at /opt/workbrew/home. Most services work that way. Some need your own account instead, because they read files only you can open, keep their settings in your home folder, or draw on your screen. Colima ignoring ~/.colima, a menu bar app that starts but never appears, or Syncthing that can't read a folder you own are all signs of this.
This guide shows how to run such a service from a LaunchAgent of your own. The service still comes from Homebrew, so brew upgrade keeps updating it. What changes when brew runs as the workbrew account explains why brew services works this way.
Stop the service under brew services
If you already started the service with brew services, stop it so two copies don't run at once.
brew services stop <formula>
Find the command the service runs
Homebrew prints it in the formula's caveats, after "if you don't want/need a background service you can just run". To see the caveats again, run:
brew info <formula>
For Syncthing, the command is:
/opt/homebrew/opt/syncthing/bin/syncthing --no-browser --no-restart
Keep the /opt/homebrew/opt/<formula> path rather than a path with a version number in it. That path follows upgrades.
Write the LaunchAgent
Save a plist in ~/Library/LaunchAgents. Name it, and give it a label, that doesn't start with homebrew.mxcl. or sh.brew.. Workbrew stops and removes Homebrew's own service plists from users' LaunchAgents when it takes over Homebrew on a Device and when it updates, so a plist with one of those names won't last. local.<formula> works.
This example is ~/Library/LaunchAgents/local.syncthing.plist. Put the command from the previous step in ProgramArguments, one item per word.
<?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>local.syncthing</string>
<key>ProgramArguments</key>
<array>
<string>/opt/homebrew/opt/syncthing/bin/syncthing</string>
<string>--no-browser</string>
<string>--no-restart</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
</dict>
</plist>
RunAtLoad starts it when you log in, and KeepAlive restarts it if it exits. Drop KeepAlive for a service that should run once and stop.
Load it
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/local.syncthing.plist
From then on it starts each time you log in. To check that it runs as you:
ps -axo user,command | grep syncthing
After an upgrade
brew upgrade replaces the files but leaves the running copy alone. Restart it to pick up the new version.
launchctl kickstart -k gui/$(id -u)/local.syncthing
Clean up the old copy
The copy that ran under brew services kept its settings and data under /opt/workbrew/home, separate from yours. Those files belong to the workbrew account, so removing them needs an administrator and sudo. Leaving them in place does no harm while the service is stopped.
To go back to brew services, unload your LaunchAgent and delete it first.
launchctl bootout gui/$(id -u)/local.syncthing
rm ~/Library/LaunchAgents/local.syncthing.plist
Related docs
- Troubleshooting - other
brew servicesand permission problems - What changes when brew runs as the workbrew account - why
brew servicesruns asworkbrew - How to configure PostgreSQL for local development - when the service can stay under
brew services - What changes on a Device when Workbrew manages Homebrew - why existing services stop at the switch
- Managed brew access - getting
brewaccess as a Standard User