FAQ

Gateway ports, already running, and remote mode

Gateway: ports, "already running", and remote mode

What port does the Gateway use?

gateway.port controls the single multiplexed port for WebSocket + HTTP (Control UI, hooks, etc.). Precedence:

text
--port > OPENCLAW_GATEWAY_PORT > gateway.port > default 18789
Why does openclaw gateway status say "Runtime: running" but "Connectivity probe: failed"?

"Running" is the supervisor's view (launchd/systemd/schtasks); the connectivity probe is the CLI actually connecting to the gateway WebSocket. Trust these lines from openclaw gateway status: Probe target: (the URL the probe used), Listening: (what is actually bound on the port), Last gateway error: (common root cause when the process is alive but the port is not listening).

Why does openclaw gateway status show "Config (cli)" and "Config (service)" different?

You are editing one config file while the service runs another (often a --profile / OPENCLAW_STATE_DIR mismatch).

Fix, run from the same --profile / environment you want the service to use:

bash
openclaw gateway install --force
What does "another gateway instance is already listening" mean?

OpenClaw enforces a runtime lock by binding the WebSocket listener immediately on startup (default ws://127.0.0.1:18789). If the bind fails with EADDRINUSE, it throws GatewayLockError ("another gateway instance is already listening").

Fix: stop the other instance, free the port, or run with openclaw gateway --port <port>.

How do I run OpenClaw in remote mode (client connects to a Gateway elsewhere)?

Set gateway.mode: "remote" and point to a remote WebSocket URL, optionally with shared-secret remote credentials:

json5
{  gateway: {    mode: "remote",    remote: {      url: "ws://gateway.tailnet:18789",      token: "your-token",      password: "your-password",    },  },}
  • openclaw gateway only starts when gateway.mode is local (or you pass an override flag).
  • The macOS app watches the config file and switches modes live when these values change.
  • gateway.remote.token / .password are client-side remote credentials only; they do not enable local gateway auth by themselves.
The Control UI says "unauthorized" (or keeps reconnecting). What now?

Your gateway auth path and the UI's auth method do not match.

Facts (from code):

  • The Control UI keeps the token in sessionStorage, scoped to the current browser tab and selected gateway URL, so same-tab refreshes keep working without long-lived localStorage token persistence.
  • On AUTH_TOKEN_MISMATCH, trusted clients can attempt one bounded retry with a cached device token when the gateway returns retry hints (canRetryWithDeviceToken=true, recommendedNextStep=retry_with_device_token).
  • That cached-token retry reuses the cached approved scopes stored with the device token; explicit deviceToken / explicit scopes callers keep their requested scope set instead of inheriting cached scopes.
  • Outside that retry path, connect auth precedence is explicit shared token/password first, then explicit deviceToken, then stored device token, then bootstrap token.
  • Built-in setup-code bootstrap returns a node device token with scopes: [] plus a bounded operator handoff token for trusted mobile onboarding. The operator handoff can read setup-time native configuration but does not grant pairing mutation scopes or operator.admin.

Fix:

  • Fastest: openclaw dashboard (prints + copies the dashboard URL, tries to open; shows an SSH hint if headless).
  • No token yet: openclaw doctor --generate-gateway-token.
  • Remote: tunnel first with ssh -N -L 18789:127.0.0.1:18789 user@host, then open http://127.0.0.1:18789/.
  • Shared-secret mode: set gateway.auth.token / OPENCLAW_GATEWAY_TOKEN or gateway.auth.password / OPENCLAW_GATEWAY_PASSWORD, then paste the matching secret in Control UI settings.
  • Tailscale Serve mode: confirm gateway.auth.allowTailscale is enabled and you are opening the Serve URL, not a raw loopback/tailnet URL that bypasses Tailscale identity headers.
  • Trusted-proxy mode: confirm you are coming through the configured identity-aware proxy. Same-host loopback proxies also need gateway.auth.trustedProxy.allowLoopback = true.
  • Mismatch persists after the one retry: rotate/re-approve the paired device token:
    bash
    openclaw devices listopenclaw devices rotate --device <id> --role operator
  • Rotate denied: paired-device sessions can rotate only their own device unless they also have operator.admin, and explicit --scope values cannot exceed the caller's current operator scopes.
  • Still stuck: openclaw status --all plus Troubleshooting. See Dashboard for auth details.
I set gateway.bind tailnet but it listens only on loopback

tailnet bind picks a Tailscale IP from your network interfaces (100.64.0.0/10). If the machine is not on Tailscale (or the interface is down), the Gateway falls back to loopback instead of exposing another network interface.

Fix: start Tailscale on that host and restart the Gateway, or switch explicitly to gateway.bind: "loopback" / "lan".

tailnet is explicit; auto prefers loopback. Use gateway.bind: "tailnet" to limit non-loopback exposure to the Tailnet while retaining the required same-host 127.0.0.1 listener.

Can I run multiple Gateways on the same host?

Usually no - one Gateway can run multiple messaging channels and agents. Use multiple Gateways only for redundancy (for example a rescue bot) or hard isolation, and isolate each with its own OPENCLAW_CONFIG_PATH, OPENCLAW_STATE_DIR, agents.defaults.workspace, and unique gateway.port.

Recommended: openclaw --profile <name> ... per instance (auto-creates ~/.openclaw-<name>), a unique gateway.port per profile config (or --port for manual runs), and a per-profile service with openclaw --profile <name> gateway install.

Profiles also suffix service names: launchd ai.openclaw.<profile>, systemd openclaw-gateway-<profile>.service, Windows OpenClaw Gateway (<profile>). The unqualified openclaw-gateway systemd unit only exists for the default profile; the legacy pre-rename systemd unit name clawdbot-gateway is migrated automatically.

Full guide: Multiple gateways.

What does "invalid handshake" / code 1008 mean?

The Gateway is a WebSocket server and expects the first message to be a connect frame. Anything else closes the connection with code 1008 (policy violation).

Common causes: you opened the HTTP URL in a browser instead of a WS client, used the wrong port/path, or a proxy/tunnel stripped auth headers or sent a non-Gateway request.

Fix: use the WS URL (ws://<host>:18789, or wss://... over HTTPS), do not open the WS port in a normal browser tab, and include the token/password in the connect frame when auth is on. CLI/TUI example:

bash
openclaw tui --url ws://<host>:18789 --token <token>

Protocol details: Gateway protocol.

Was this useful?
On this page

On this page