I’m tired of my Mac Mini powering down when I’m remote. This is what I did to fix it.
The setup: my agent Brook runs on a Mac Mini M4 in my home office, on Hermes Agent, talking to me over Telegram and email. I SSH into the box through Tailscale from wherever I am. The whole point is that she’s always on.
This morning I found her dead. The gateway process took a SIGTERM overnight and nothing brought it back. No morning briefing, no check-ins, just silence until I noticed and restarted it by hand.
That was the last time. Here’s the fix, in two layers.
#Layer 1: Stop the Mac from sleeping or staying down
Three pmset commands handle the hardware layer:
sudo pmset -a sleep 0
sudo pmset -a autorestart 1
sudo pmset -a womp 1
sleep 0- the machine never sleeps. Display sleep is fine, system sleep kills every process you care about.autorestart 1- the Mini powers itself back on after a power outage. This is the one most people miss.womp 1- wake on network access, so a Tailscale SSH attempt can reach it.
Check your current state with pmset -g. Mine was already mostly right. The process layer was the real problem.
#Layer 2: Make launchd babysit the process
A long-running agent started from a terminal is just a loose process. Close the session wrong, or let it crash at 2am, and it’s gone until a human shows up.
macOS already ships the fix: launchd, the same init system that starts everything else on the machine. You hand it a plist and it owns your process. Reboot? It starts it. Crash? It restarts it in seconds. Nobody logged in? Doesn’t matter.
The plist goes in /Library/LaunchDaemons/ (system-wide daemon, not a user LaunchAgent - more on that below). Mine looks like this, trimmed to the parts that matter:
<key>Label</key>
<string>ai.hermes.gateway</string>
<key>UserName</key>
<string>max</string>
<key>ProgramArguments</key>
<array>
<string>/Users/max/.hermes/hermes-agent/venv/bin/python</string>
<string>-m</string>
<string>hermes_cli.main</string>
<string>gateway</string>
<string>run</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<false/>
</dict>
<key>ThrottleInterval</key>
<integer>10</integer>
RunAtLoad- starts the process at boot. Survives power outages when paired withautorestart.KeepAlivewithSuccessfulExit: false- launchd restarts the process any time it exits with a non-zero code. Crashes and kills get revived. A clean intentional shutdown stays down.ThrottleInterval- minimum seconds between restarts, so a crash loop doesn’t spin the CPU.
Install and start it:
sudo install -o root -g wheel -m 644 my-agent.plist /Library/LaunchDaemons/ai.hermes.gateway.plist
sudo launchctl bootstrap system /Library/LaunchDaemons/ai.hermes.gateway.plist
sudo launchctl kickstart -k system/ai.hermes.gateway
#The gotcha that almost got me
My bootstrap command failed with Bootstrap failed: 5: Input/output error. Looks fatal. It isn’t.
Run launchctl print system/ai.hermes.gateway and check. In my case the service had registered anyway, it just hadn’t started. The kickstart line was all it needed. Don’t re-run bootstrap in a loop trying to make the error go away.
Two more traps that will bite you:
- LaunchAgent vs LaunchDaemon. A plist in
~/Library/LaunchAgents/only loads inside a GUI login session. SSH doesn’t count. If your box sits headless, you want/Library/LaunchDaemons/with aUserNamekey. - Test it for real. Find the PID and
killit. Mine was back in about a second, reconnected to Telegram and Gmail on its own. If you didn’t watch it resurrect, you don’t know it works.
#Driving it from anywhere
None of this matters if I have to be home to use it. Tailscale gives every device I own a stable address on a private network, so the Mini is one ssh away from my laptop or my phone, from any network, no port forwarding.
Once I’m in, launchd takes orders:
# restart Hermes (kill and relaunch in one shot)
sudo launchctl kickstart -k system/ai.hermes.gateway
# stop it on purpose, and keep it down
sudo launchctl bootout system/ai.hermes.gateway
# bring it back
sudo launchctl bootstrap system /Library/LaunchDaemons/ai.hermes.gateway.plist
# check on it
launchctl print system/ai.hermes.gateway
kickstart -k is the one I actually use. Config change, stuck process, weird behavior after an update - one command and Hermes restarts clean, reconnected to Telegram in seconds.
One warning from experience: never start a second copy by hand with hermes gateway run while the daemon is live. Two gateways means two processes polling the same Telegram bot, and they fight over every message. If launchd owns the process, let launchd restart it.
And if the Mini ever needs more than a process restart, sudo reboot over SSH works fine. autorestart and RunAtLoad mean the machine comes back, and Hermes comes back with it.
Brook has now survived a deliberate kill, and the next power outage is already handled. The Mini sits at home, I’m out living my life, and nobody has to drive back to push a button.
Let’s go.