I lost an embarrassing amount of time blaming the wrong layer.
At 6 p.m., my local agent stack looked fine.
The Mac mini was watching a repo, pushing jobs into n8n, fanning work out to downstream workers, and occasionally calling GPT-5 for planning and Claude Opus 4.6 for cleanup. Everything looked healthy.
By morning, the logs were dead.
No obvious crash. No stack trace. Just silence after around 1 a.m., like macOS had quietly decided my automations were no longer invited.
I blamed the models first.
Then n8n.
Then Redis.
Then Watchman.
Then my own code.
All of those can fail.
But on a headless Mac mini, the first thing to suspect is usually much more boring:
macOS power state and launchd setup.
If you need to keep a Mac mini awake headless, this is the stuff that actually matters.
The symptom that fooled me
A headless Mac mini can still respond on the network and look vaguely alive even when the worker you care about is gone.
That’s what makes this annoying.
You can still SSH in sometimes. Ping works. The machine exists. But your agent, watcher, or queue worker has stopped doing useful work.
The key detail is that on macOS, display sleep and system sleep are not the same thing.
If you only think in terms of “the screen turned off,” you can debug the wrong problem for days.
Display sleep vs system sleep on macOS
These are separate settings:
-
displaysleepcontrols when the display sleeps -
sleepcontrols idle system sleep
So yes, your Mac mini can have the display off and still be fine.
But it can also enter idle sleep and take your automation stack down with it.
Start by checking the actual power config instead of guessing.
pmset -g
pmset -g assertions
What they tell you:
-
pmset -gshows configured power settings -
pmset -g assertionsshows what is currently preventing sleep
That second command is the one that exposes a lot of fake “server” setups.
The SSH trap
I hit this one too.
You SSH into the Mac mini, run your workers, and everything stays alive. So you assume the box is configured correctly.
Not necessarily.
macOS has ttyskeepawake, which can help prevent idle sleep while a tty session is active.
That means your SSH session may be the only thing keeping the machine awake.
Once the session drops, the machine can go right back to sleeping unless:
- idle sleep is disabled
- another process is holding a sleep-preventing assertion
- you configured the system correctly for headless use
That’s why these failures feel random.
They’re usually not random.
Your terminal session was acting like life support.
The launchd mistake that breaks "always-on" workers
The next problem is launchd.
This one is sneaky because it often works perfectly during the day.
A lot of people start with a LaunchAgent because it’s easy to test from a logged-in user session.
That’s fine for user-scoped automation.
It’s a bad fit for infrastructure-y workers that need to survive reboots, logouts, and no attached display.
Here’s the practical difference:
| Option | What it really means |
|---|---|
| LaunchAgent | Per-user job. Runs in a user session. Can disappear when the user logs out or when the GUI session changes |
| LaunchDaemon | System-level job. Better fit for always-on background workers not tied to an interactive login |
If your agent runner only works while you’re logged into Finder or Terminal, it is not actually always-on.
It’s a desktop app in denial.
When to use LaunchDaemon instead
If the process needs to:
- survive reboots
- run with no monitor attached
- keep processing jobs after logout
- behave like a service instead of a personal script
then it probably wants to be a LaunchDaemon.
Not everything belongs there.
But if you’re running queue workers, repo indexers, bot processes, or OpenClaw-style automations 24/7, tying them to a user session is asking for overnight failures.
The other failure mode: the process is alive, but the workflow is dead
This is where file watchers make things confusing.
A lot of local agent stacks depend on file changes:
- repo changes happen
- Watchman sees them
- indexing or build logic runs
- n8n picks up downstream work
- model calls happen after that
When the chain breaks, people often blame the LLM call because that’s the visible step.
But the failure can happen much earlier.
On macOS, Watchman can run into FSEvents issues after sleep/wake or event backlog. When that happens, you can get recrawls, dropped events, or delayed triggers.
That can look exactly like "my agent got flaky overnight."
What actually happened is your watcher got wrecked.
Watchman setting worth testing
If you depend on Watchman on macOS, this is worth trying:
{
"fsevents_latency": 0.5
}
Put that in .watchmanconfig, then restart Watchman:
watchman shutdown-server
Things to watch for:
- recrawl warnings
- delayed file triggers
- bursts of false-positive changed files after wake
- downstream jobs that look stuck but are really waiting on the watcher layer
If you’re seeing recrawl warnings constantly, don’t just suppress them and move on. Understand why they’re happening first.
What "screen off killed my agents" usually really means
In practice, that sentence usually maps to one of four root causes:
- The Mac entered idle system sleep, not just display sleep
- The worker was started as a
LaunchAgent, so it depended on the user session - An SSH session was the only thing keeping the machine awake
- Watchman or another file watcher fell behind after sleep/wake and broke downstream automation
Different root causes, same symptom:
"Everything worked yesterday and now the pipeline is dead."
My baseline fix for a headless Mac mini
This is the first setup I wish I had done on day one.
sudo pmset -c sleep 0
sudo pmset -c displaysleep 10
sudo pmset -c ttyskeepawake 1
What those do:
-
sleep 0disables idle system sleep while on charger power -
displaysleep 10still allows display sleep separately -
ttyskeepawake 1lets active tty sessions like SSH help prevent sleep
Then verify the result:
pmset -g
pmset -g assertions
And don’t stop there.
Do these checks too:
- move critical workers from
LaunchAgenttoLaunchDaemonif they truly need to be always-on - send logs somewhere readable without a GUI session
- test after a full reboot with no interactive login
- test overnight without leaving an SSH session open
- if you use Watchman, inspect recrawls and FSEvents warnings after sleep/wake
That last one matters.
If the setup only works while Terminal is open, it’s not a server.
It’s a supervised demo.
Why n8n and agent workflows expose this faster
n8n makes these problems obvious because reliability is the product.
If you’re just tinkering during the day, a single local instance can feel fine.
But once you start doing real automation work, the stack grows fast:
- n8n
- Redis
- workers
- file watchers
- scheduled jobs
- repo sync
- multiple model calls
- retries and queueing
At that point, OS behavior becomes more important than model quality.
That’s the part people underestimate.
Developers love arguing about GPT-5 vs Claude Opus 4.6 vs Grok 4.20.
Sure, model choice matters.
But if your Mac mini sleeps, drops the user session, or breaks the watcher chain overnight, none of that matters.
Your benchmark winner is now doing zero work.
The real lesson: fix the machine before you blame the model
The most annoying part of self-hosted agent setups is that the hard part often isn’t AI.
It’s the OS.
It’s whether your “server” is actually:
- a sleeping desktop
- running a per-user background job
- depending on an active SSH session
- using a watcher that falls apart after wake
That’s much less exciting than model evals.
It’s also the difference between an agent that runs for a week and one that quietly dies every night.
Practical checklist
If your Mac mini is running automations, agents, or local workers, this is the checklist I’d use:
# Inspect current power settings
pmset -g
# Inspect active sleep-prevention assertions
pmset -g assertions
# Disable idle system sleep on charger power
sudo pmset -c sleep 0
# Keep display sleep separate
sudo pmset -c displaysleep 10
# Let active SSH/tty sessions help keep the system awake
sudo pmset -c ttyskeepawake 1
Then verify architecture, not just commands:
- critical worker uses
LaunchDaemon, notLaunchAgent - logs are available without GUI login
- reboot test passes
- overnight test passes with no SSH session
- watcher layer is healthy
One more thing if you’re scaling agent workflows
If you’re building automations that call multiple models all day, the Mac mini problem is really a smaller version of a bigger ops problem.
Once you have background workers constantly routing jobs between tools, the expensive part is no longer just model quality. It’s reliability and cost predictability.
That’s why teams eventually move away from babysitting token spend and start caring more about stable, always-on execution. If your workflows are hammering GPT-5, Claude, and Grok through automations, predictable infrastructure matters as much as prompts do.
That’s also why services like Standard Compute exist: same OpenAI-compatible API shape, but built for agent-heavy workloads that run all the time without per-token panic. If your automations are finally stable, the next bottleneck is usually cost.
Final takeaway
If your headless Mac mini keeps "randomly" killing agents overnight, start here before touching prompts, model routing, or workflow logic:
- inspect
pmset - inspect sleep assertions
- stop depending on SSH as your wake strategy
- move always-on workers to
LaunchDaemon - check Watchman and file-watcher health
My actual fix was boring.
And that was the whole point.
Fix the OS before you blame the LLM.
Top comments (0)