Teams increasingly park OpenClaw on a dedicated remote Mac so webhooks, MCP children, and long-lived queues stay off developer laptops. The boring failure mode is not “bad YAML” — it is PATH drift, double NAT, and a Gateway that only answers when someone is logged into VNC. Colocating with your build fleet also cuts round trips: the same machine can host self-hosted runners while the Gateway stays on loopback, which keeps blast radius smaller than exposing a cloud VM to the public internet. This note walks a reproducible path: native install with Node 22, an onboard pass that materialises directories before doctor complains, SSH tunnel patterns that expose the listener without a public bind, and a compact FAQ for the doctor tickets you will actually file. For transport-level MCP tuning once the tunnel is stable, pair this with
OpenClaw MCP stdio vs Streamable HTTP, timeouts, and ENOENT triage;
for fleet-style runners and cache disks, see
multi-Mac self-hosted runners, Actions cache, and persistent disk hygiene.
1. Baseline the host: Apple Silicon, Xcode CLT, and Node 22 you can plist
Install Command Line Tools or full Xcode so git, security, and notarisation helpers exist. Pin Node 22 with an absolute interpreter path (/opt/homebrew/opt/node@22/bin/node or your nvm prefix) and verify node -v matches what doctor prints — mismatches between an interactive shell and a launchd job account for half the “works on my machine” regressions. If you upgrade Node during a live experiment, roll the plist, reload the agent, and only then rerun doctor; otherwise you will chase phantom permission errors that are really argv drift. Keep OpenClaw state on local APFS outside Desktop/Documents mirrors; WAL-heavy stores and iCloud are a predictable source of flaky locks.
ProgramArguments for the daemon that will actually run at 03:00.2. Native install order: onboard before doctor
onboard creates expected folders, seeds example configs, and aligns plugin entries with the filesystem layout your build expects. Running doctor first produces noisy false positives: missing sockets, unreadable state roots, and “ENOENT” for binaries that simply were not linked yet. Treat onboard as provisioning and doctor as verification — capture both outputs in the same ticket so support can diff them. If you import ClawHub skills, trim plugins.entries to the smallest surface before you automate, mirroring the least-privilege posture described in the permissions-focused OpenClaw articles.
3. SSH tunnels for Gateway reachability
Most teams want the Gateway listening on 127.0.0.1 inside the Mac and reachable from a bastion or laptop. Use LocalForward on your client when you need “my laptop talks to remote localhost:8787”. Use RemoteForward when a central collector must dial back through the SSH session without opening inbound firewall rules on the data center edge. Combine with GatewayBind=loopback-style settings (however your version names it) so you never accidentally publish MCP on 0.0.0.0. Keep ServerAliveInterval and TCPKeepAlive tuned so CI jobs do not inherit silent half-open tunnels.
ssh -N -L 8787:127.0.0.1:8787 user@remote-mac maps your laptop’s 8787 to the remote Gateway. Swap -R when the far side must originate the dial path through the same SSH session; document which host runs curl health checks so on-call does not test the wrong interface.4. Daemonising onboard helpers and the Gateway with launchd
Use a user LaunchAgent for Gateway-shaped workloads, set WorkingDirectory to the repo root, export a minimal PATH, and log stdout/stderr under ~/Library/Logs. Run launchctl bootstrap gui/$UID after edits. If you wrap a one-shot onboard in a plist, mark it RunAtLoad once, then disable to avoid clobbering intentional drift; most teams run onboard manually on upgrades and keep only the Gateway supervised.
When several operators share a remote Mac, prefer per-user agents over a single root daemon unless you truly need system scope — root-owned jobs hide permission prompts that TCC would otherwise surface early.
5. doctor FAQ: the errors that survive tunnels
ENOENT for child MCP servers: fix cwd, argv[0], and PATH inside the plist — not the tunnel. Port already in use: only one Gateway may bind a port; retire stray launchd jobs with launchctl print. TLS or webhook mismatch: confirm the public URL seen by GitHub matches the forwarded host header; curl locally with -v through the tunnel before blaming OpenClaw. Timeouts under bursty tools: raise MCP timeouts after you validate transport, otherwise you will chase ghosts while Xcode peaks CPU.
“Healthy” locally but webhooks fail: verify traffic hits the forwarded port, not a stale LAN IP. Disk pressure: prune logs and DerivedData before you misread IO stalls as OpenClaw bugs.
6. Pre-flight checklist for “remote Mac is production”
- Node 22 path in plist matches
doctor; reboot test passes without an interactive login. - State directory is local, backed up explicitly, not cloud-synced.
- SSH tunnel maps the exact loopback port your Gateway binds; health checks curl through the tunnel.
- Heavy Xcode or simulator bursts have a larger worker or schedule window so the Gateway stays responsive.
Why Mac mini class hardware still anchors this pattern
SSH-forwarded Gateways need stable NICs, predictable thermals, and quiet disks more than peak burst clocks. A Mac mini on Apple Silicon keeps idle power in the low single-digit watts while exposing the same Unix surface your CI fleet already trusts. macOS stacks Gatekeeper, SIP, and FileVault beside the SSH and code-signing tools you rely on for unattended jobs, which is simpler than hardening a generic PC for the same role. Native Homebrew, Docker Desktop patterns, and rich logging fit the operational model described here without extra translation layers.
If you want that profile without sourcing another desk machine, a dedicated Mac mini M4 close to your users gives you loopback-only listeners, resilient tunnels, and room for Xcode when jobs spike — start from the Macstripe home page to match region, bandwidth, and memory to your OpenClaw load.