# Gateway ports, already running, and remote mode

> Gateway ports, remote-mode clients, bind addresses, and running more than one Gateway

- 网址：https://funcoding.ai/agents/openclaw/help/faq/gateway-ports-and-remote-mode/
- 来源：OpenClaw 官方文档原文（英文），MIT 许可，同步于 2026-10-11
- 官方原文：https://docs.openclaw.ai/zh-CN/help/faq/gateway-ports-and-remote-mode

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

<details>
<summary>What port does the Gateway use?</summary>

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

```text
--port > OPENCLAW_GATEWAY_PORT > gateway.port > default 18789
```

</details>

<a id="why-does-openclaw-gateway-status-say-runtime-running-but-connectivity-probe-failed" />

<details>
<summary>Why does openclaw gateway status report a running process but a failed connection?</summary>

"Running" is the **supervisor's** view (launchd/systemd/schtasks); the connectivity check is the CLI actually connecting to the gateway WebSocket. In `openclaw gateway status`, compare the connection target URL with `Listening:` (what is actually bound on the port), and inspect `Last gateway error:` for the failure cause when the process is alive but the port is not listening.

</details>

<details>
<summary>Why does openclaw gateway status show "Config (cli)" and "Config (service)" different?</summary>

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
```

</details>

<details>
<summary>What does "another gateway instance is already listening" mean?</summary>

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>`.

</details>

<details>
<summary>How do I run OpenClaw in remote mode (client connects to a Gateway elsewhere)?</summary>

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.

</details>

<details>
<summary>The Control UI says "unauthorized" (or keeps reconnecting). What now?</summary>

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 list
  openclaw 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](https://funcoding.ai/agents/openclaw/gateway/troubleshooting/). See [Dashboard](https://funcoding.ai/agents/openclaw/web/dashboard/) for auth details.

</details>

<details>
<summary>I set gateway.bind tailnet but it listens only on loopback</summary>

`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.

</details>

<details>
<summary>Can I run multiple Gateways on the same host?</summary>

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](https://funcoding.ai/agents/openclaw/gateway/multiple-gateways/).

</details>

<details>
<summary>What does "invalid handshake" / code 1008 mean?</summary>

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](https://funcoding.ai/agents/openclaw/gateway/protocol/).

</details>
