OpenClaw 2026 remote Gateway pairing with SSH tunnels: macOS client health checks, LaunchAgent persistence, and troubleshooting
You already run OpenClaw Gateway on a JEXCLOUD bare-metal Mac, yet the macOS app on your laptop stalls on Health check pending or gateway.remote pairing failures. In 2026 the most common root cause is not “Gateway is down,” but a broken link in local OpenClaw app → SSH tunnel → remote 127.0.0.1:18789. This guide targets developers and SREs who need the local OpenClaw client to control a remote Gateway, and delivers an SSH tunnel versus Direct WSS decision matrix, LaunchAgent persistence checklist, health patrol scripts, and a troubleshooting table (configuration details align with the OpenClaw official onboarding documentation).
When you finish reading, you should be able to answer three questions: (1) whether your network should use SSH LocalForward or Direct wss://; (2) how OPENCLAW_GATEWAY_TOKEN relates to the two LaunchAgents ai.openclaw.ssh-tunnel and ai.openclaw.gateway; (3) when CLI openclaw health passes but the UI still shows pending, whether to inspect ports, tokens, or the tunnel process first.
01 OpenClaw remote Gateway architecture: macOS client controlling a cloud Mac
In 2026 OpenClaw on macOS exposes two Gateway connection modes: Local (Gateway and app on the same machine) and Remote (Gateway on another Mac; the laptop app reaches it through an SSH tunnel or a direct WebSocket). Production setups often place Gateway on a JEXCLOUD bare-metal Mac bound to 127.0.0.1:18789, while the developer laptop runs OpenClaw.app as a control surface only. That split pairs with our companion article on node selection and launchd token troubleshooting: that piece answers how to install and size the remote host; this piece answers how to connect from your Mac, keep the tunnel alive, and prove liveness.
Remote mode assumes the Gateway process never needs a public listener. The laptop never talks to the instance’s public IP for Gateway traffic; it talks to localhost:18789 after SSH forwards that port. That design shrinks attack surface and matches how most teams already access cloud Macs through SSH keys documented in the help center. When pairing fails, teams often reinstall Gateway on the server even though the daemon is healthy, because the failure is on the path between UI and loopback, not inside the Gateway binary itself.
The mental model is a three-hop chain. Hop one is the macOS OpenClaw UI issuing health and control calls against what it believes is a local Gateway. Hop two is the SSH process on the laptop translating those calls through an encrypted session. Hop three is the Gateway daemon on the cloud Mac accepting connections only on loopback. If any hop is misconfigured, symptoms cluster around pending health states or pairing errors even when a direct SSH shell session to the host still works fine.
In architecture reviews, these five pain points are routinely underestimated:
- Ephemeral tunnels: A manual
ssh -Ndies when the laptop sleeps or the terminal closes. The app may show stale state until a health probe times out, even though Gateway never restarted. - Dual-track tokens: Mismatches among remote
launchctlenvironment variables, files under~/.openclaw, and the token typed in the macOS app trigger pairing or device-token class errors that look like “remote is broken.” - Port and forward misalignment: The default Gateway port 18789 may be occupied, or
LocalForwardmay point at the wrong target. CLI checks against the remote host succeed whilecurl http://127.0.0.1:18789on the laptop fails. - Health Check UI lag: Known cases where CLI health passes but the macOS UI remains on pending (community discussion in openclaw#66306).
- Security exposure: Binding Gateway to
0.0.0.0without mTLS on a public IP is equivalent to exposing an authenticated control plane. The recommended pattern is 127.0.0.1 plus SSH or Tailscale.
Operations that skip loopback binding often do so to avoid SSH friction, then spend more time on incident response than tunnel maintenance would have cost. The loopback-plus-tunnel model also keeps audit boundaries clear: SSH key rotation lives in standard identity tooling; Gateway tokens stay on the Mac that runs the daemon.
Memory line for on-call: Gateway listens on loopback only; the tunnel carries egress; pick one token source of truth. Violating any leg of that sentence usually turns debugging into multi-hour hunts.
02 SSH tunnel or Direct WSS: OpenClaw remote transport decision matrix
Under OpenClaw runs in macOS settings, transport is typically SSH tunnel versus Direct (ws/wss). Neither option is universally correct; the right choice matches your firewall posture, Tailscale coverage, and who maintains certificates.
SSH tunneling reuses infrastructure most Mac teams already operate: ~/.ssh/config, key-based login, and optional LaunchAgents. Direct WSS shines when a platform team already terminates TLS on a stable hostname and clients can reach it without corporate proxy interference. Hybrid teams sometimes run SSH for individual developers and WSS for shared automation accounts. Document both paths so on-call does not guess which transport an incident used.
When you evaluate Direct WSS, run the same health probes you will use in production (Bearer token against /health) from every network profile you care about: office VPN, home ISP, and mobile hotspot. A transport that works on Tailscale but fails on guest Wi-Fi becomes a support ticket factory. SSH tunneling trades a small latency tax for predictability because it piggybacks on the same SSH path you already use for deploys and log tailing.
| Dimension | SSH tunnel + LocalForward | Direct WSS |
|---|---|---|
| Security boundary | Gateway stays on 127.0.0.1; traffic exits over encrypted SSH | Requires TLS termination and token discipline; often paired with Tailscale or private DNS |
| Firewall friendliness | Only SSH (port 22 or custom) must be reachable | Must allow WSS ports and trust certificate chains; corporate proxies often break this path |
| Latency and stability | Extra SSH hop; RTT of 15–40 ms is usually acceptable; KeepAlive plists recover drops | One fewer hop when a stable WSS reverse proxy already exists |
| Operational complexity | Maintain ~/.ssh/config plus a tunnel LaunchAgent |
Maintain certificates, reverse proxy, and token rotation runbooks |
| Recommended scenario | Solo developers or small teams remoting into a single JEXCLOUD node without dedicated SRE | Larger teams with Tailscale Serve or a unified API gateway already in production |
If you already enabled SSH key login through the help center and the remote Gateway binds only to loopback, default to SSH tunneling. Switch to Direct only when the organization provides a stable wss://gateway.example.ts.net endpoint and macOS Test remote passes consistently across networks you care about.
03 Cloud Mac prerequisites: PATH, token permissions, and SSH snippets
The remote host must run OpenClaw CLI in non-interactive shells. After pnpm link --global, symlink the binary into /opt/homebrew/bin or /usr/local/bin so launchd jobs inherit PATH. Token files should be mode 600 and owned by the same user that runs Gateway.
On the laptop, treat ~/.ssh/config as version-controlled infrastructure. Use a dedicated Host alias per environment so LaunchAgent plists stay short and auditable. Keep IdentityFile paths stable; moving keys without updating plists produces silent tunnel failures that look like Gateway outages.
Host jex-openclaw-sg
HostName your-instance.jexcloud.com
User macuser
IdentityFile ~/.ssh/jexcloud_ed25519
LocalForward 18789 127.0.0.1:18789
ServerAliveInterval 30
ServerAliveCountMax 3
<key>Label</key>
<string>ai.openclaw.ssh-tunnel</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/ssh</string>
<string>-N</string>
<string>jex-openclaw-sg</string>
</array>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
Gateway itself is installed via the macOS app or CLI as LaunchAgent ai.openclaw.gateway. Inject OPENCLAW_GATEWAY_TOKEN through launchctl setenv or plist EnvironmentVariables; avoid duplicating the same secret in shell profiles and plists, which drifts within weeks.
Before pairing, verify from the laptop that forwarding works in isolation: start a foreground ssh -N jex-openclaw-sg, then probe localhost. Only after HTTP 200 should you load the tunnel LaunchAgent. Loading plists first often hides SSH auth errors inside launchd logs that developers do not know to read.
04 Six-step OpenClaw remote Gateway pairing: from Test remote to LaunchAgent verification
Follow these steps in order during initial pairing and after credential rotation. Skipping verification on the laptop loopback before opening the app is the most common source of false “remote broken” escalations.
- Install CLI and start Gateway on the remote host: On your JEXCLOUD instance run
openclaw gateway install(or follow in-app LaunchAgent setup). Confirmlsof -i :18789shows listen on127.0.0.1only. - Write and validate the token: Generate a Gateway token, save with
chmod 600, thenlaunchctl setenv OPENCLAW_GATEWAY_TOKEN '…'and restart the Gateway agent. - Configure SSH on the laptop: Add
LocalForward 18789to~/.ssh/config, runssh -N jex-openclaw-sgin a test session, and in another terminal runcurl -s -o /dev/null -w '%{http_code}' -H "Authorization: Bearer $TOKEN" http://127.0.0.1:18789/health; expect 200. - Install the tunnel LaunchAgent:
launchctl bootstrap gui/$UID ~/Library/LaunchAgents/ai.openclaw.ssh-tunnel.plist, then confirm the tunnel starts automatically after login. - Pair in the macOS client: Settings → OpenClaw runs → Remote + SSH tunnel, enter
user@host(or the config Host alias alone if you rely on~/.ssh/config), then run Test remote until it succeeds. - Register patrol and alerting: Schedule the health script on
StartInterval(300 seconds is a reasonable default) or via a CI SSH job. On failure,launchctl kickstart -k gui/$UID/ai.openclaw.ssh-tunneland record exit codes for on-call.
After pairing succeeds, walk through an onboarding session inside the app to validate the Agent channel end to end. If only CLI works while the UI stays pending, use the matrix in the next section instead of reinstalling Gateway on the server.
For credential rotation, rotate in this order: update token on the remote host and restart ai.openclaw.gateway, update the laptop environment used by health scripts, update the macOS app field, then rerun Test remote. Reversing the order produces short windows where CLI and UI disagree about authorization state.
05 Health Check patrol scripts and citable technical data
Production environments should assert HTTP 200 programmatically instead of trusting UI status lights. The items below are suitable for architecture reviews and runbooks (ranges reflect 2026 community practice and JEXCLOUD node observations; they are not SLA commitments).
- Default Gateway port:
18789.LocalForwardmust match; plan additional ports when running multiple Gateway instances per host. - Health endpoint: Commonly
/healthwith a Bearer token; monitoring should exit non-zero on any non-200 response. - SSH tunnel self-healing: LaunchAgent
KeepAliveplusServerAliveInterval 30typically restores connectivity within 30–90 seconds after transient drops, depending on network jitter. - Asia-Pacific RTT: Developers in East Asia reaching SG, JP, or HK nodes often see 15–35 ms round-trip for CLI operations; the SSH hop usually adds under 5 ms and is negligible for interactive control.
- Token file permissions: Mode 600 is mandatory; if the file owner does not match the Gateway process user,
launchdmay refuse to load secrets without a loud error in app UI. - Parallel health signals: Combine
openclaw health, HTTP probes, andlaunchctl list | grep openclawin runbooks so on-call can tell “daemon down” from “tunnel down” in one minute.
Store patrol scripts outside the OpenClaw tree so upgrades do not delete them. Inject OPENCLAW_GATEWAY_TOKEN from the macOS Keychain or a secrets manager wrapper rather than plaintext dotfiles on shared laptops.
#!/bin/bash
TOKEN="${OPENCLAW_GATEWAY_TOKEN:?}"
CODE=$(curl -sf -o /dev/null -w '%{http_code}' \
-H "Authorization: Bearer $TOKEN" http://127.0.0.1:18789/health)
[[ "$CODE" == "200" ]] || exit 1
Wire this script to your existing pager or log aggregator. Include the SSH tunnel label in alert text so responders restart the correct LaunchAgent. When CLI health passes but HTTP against localhost fails, the incident is almost always forwarding, not Gateway configuration.
06 FAQ, Health pending matrix, and JEXCLOUD decision closure
| Symptom | Check first | Remediation |
|---|---|---|
| CLI returns 200, UI pending | App cache, transport mode, localhost forwarding | Restart the app; confirm Remote + SSH; rerun Test remote |
| pairing / token mismatch | Dual-track tokens, device token files | Unify plist and in-app token; reset pairing per official remote Gateway docs |
| Connection refused | Tunnel process, port occupancy | launchctl list | grep openclaw; inspect port 18789 |
| Tunnel drops frequently | Laptop sleep, NAT timeouts | Enable ServerAlive; use KeepAlive in the tunnel plist; avoid manual-only ssh -N |
Using home broadband or unstable Wi-Fi as the only tunnel entry often produces “works in the morning, red in the afternoon” incidents when NAT timeouts and sleep policies kill long-lived SSH sessions. Hosting Gateway on time-shared VPS or oversubscribed virtualization can yield intermittent health failures when neighbors contend for CPU. A local Mac still suffers from automatic update reboots and unattended Keychain prompts. Teams that need stable remote control from the macOS app, auditable tokens, and self-healing tunnels usually get the calmest operations by running Gateway on JEXCLOUD multi-region bare-metal Mac hosts, then using SSH loopback forwarding plus LaunchAgent persistence: dedicated Apple Silicon, roughly two-minute provisioning in typical flows, and the option to scale out M4 Pro nodes per project. See the JEXCLOUD pricing page for plans and regions; use the VNC page when you need graphical troubleshooting alongside SSH.