openclaw/docs/gateway/tailscale.md
Vincent Koc 6c49039a23
docs(gateway): batch convert callouts and fix JSON5 smart quotes
- security/index: 3 prose callouts (Note/Warning) for remote credential rules, sandbox scope, elevated mode
- tailscale: loopback Note component
- pairing: bulleted Important warning to Warning component
- openshell: host-edit warning to Warning component
- local-models: replace 13 smart quotes inside the LM Studio JSON5 example so it parses
2026-04-26 21:56:59 -07:00

5.7 KiB
Raw Permalink Blame History

summary read_when title
Integrated Tailscale Serve/Funnel for the Gateway dashboard
Exposing the Gateway Control UI outside localhost
Automating tailnet or public dashboard access
Tailscale

OpenClaw can auto-configure Tailscale Serve (tailnet) or Funnel (public) for the Gateway dashboard and WebSocket port. This keeps the Gateway bound to loopback while Tailscale provides HTTPS, routing, and (for Serve) identity headers.

Modes

  • serve: Tailnet-only Serve via tailscale serve. The gateway stays on 127.0.0.1.
  • funnel: Public HTTPS via tailscale funnel. OpenClaw requires a shared password.
  • off: Default (no Tailscale automation).

Status and audit output use Tailscale exposure for this OpenClaw Serve/Funnel mode. off means OpenClaw is not managing Serve or Funnel; it does not mean the local Tailscale daemon is stopped or logged out.

Auth

Set gateway.auth.mode to control the handshake:

  • none (private ingress only)
  • token (default when OPENCLAW_GATEWAY_TOKEN is set)
  • password (shared secret via OPENCLAW_GATEWAY_PASSWORD or config)
  • trusted-proxy (identity-aware reverse proxy; see Trusted Proxy Auth)

When tailscale.mode = "serve" and gateway.auth.allowTailscale is true, Control UI/WebSocket auth can use Tailscale identity headers (tailscale-user-login) without supplying a token/password. OpenClaw verifies the identity by resolving the x-forwarded-for address via the local Tailscale daemon (tailscale whois) and matching it to the header before accepting it. OpenClaw only treats a request as Serve when it arrives from loopback with Tailscales x-forwarded-for, x-forwarded-proto, and x-forwarded-host headers. For Control UI operator sessions that include browser device identity, this verified Serve path also skips the device-pairing round trip. It does not bypass browser device identity: device-less clients are still rejected, and node-role or non-Control UI WebSocket connections still follow the normal pairing and auth checks. HTTP API endpoints (for example /v1/*, /tools/invoke, and /api/channels/*) do not use Tailscale identity-header auth. They still follow the gateway's normal HTTP auth mode: shared-secret auth by default, or an intentionally configured trusted-proxy / private-ingress none setup. This tokenless flow assumes the gateway host is trusted. If untrusted local code may run on the same host, disable gateway.auth.allowTailscale and require token/password auth instead. To require explicit shared-secret credentials, set gateway.auth.allowTailscale: false and use gateway.auth.mode: "token" or "password".

Config examples

Tailnet-only (Serve)

{
  gateway: {
    bind: "loopback",
    tailscale: { mode: "serve" },
  },
}

Open: https://<magicdns>/ (or your configured gateway.controlUi.basePath)

Tailnet-only (bind to Tailnet IP)

Use this when you want the Gateway to listen directly on the Tailnet IP (no Serve/Funnel).

{
  gateway: {
    bind: "tailnet",
    auth: { mode: "token", token: "your-token" },
  },
}

Connect from another Tailnet device:

  • Control UI: http://<tailscale-ip>:18789/
  • WebSocket: ws://<tailscale-ip>:18789
Loopback (`http://127.0.0.1:18789`) will **not** work in this mode.

Public internet (Funnel + shared password)

{
  gateway: {
    bind: "loopback",
    tailscale: { mode: "funnel" },
    auth: { mode: "password", password: "replace-me" },
  },
}

Prefer OPENCLAW_GATEWAY_PASSWORD over committing a password to disk.

CLI examples

openclaw gateway --tailscale serve
openclaw gateway --tailscale funnel --auth password

Notes

  • Tailscale Serve/Funnel requires the tailscale CLI to be installed and logged in.
  • tailscale.mode: "funnel" refuses to start unless auth mode is password to avoid public exposure.
  • Set gateway.tailscale.resetOnExit if you want OpenClaw to undo tailscale serve or tailscale funnel configuration on shutdown.
  • gateway.bind: "tailnet" is a direct Tailnet bind (no HTTPS, no Serve/Funnel).
  • gateway.bind: "auto" prefers loopback; use tailnet if you want Tailnet-only.
  • Serve/Funnel only expose the Gateway control UI + WS. Nodes connect over the same Gateway WS endpoint, so Serve can work for node access.

Browser control (remote Gateway + local browser)

If you run the Gateway on one machine but want to drive a browser on another machine, run a node host on the browser machine and keep both on the same tailnet. The Gateway will proxy browser actions to the node; no separate control server or Serve URL needed.

Avoid Funnel for browser control; treat node pairing like operator access.

Tailscale prerequisites + limits

  • Serve requires HTTPS enabled for your tailnet; the CLI prompts if it is missing.
  • Serve injects Tailscale identity headers; Funnel does not.
  • Funnel requires Tailscale v1.38.3+, MagicDNS, HTTPS enabled, and a funnel node attribute.
  • Funnel only supports ports 443, 8443, and 10000 over TLS.
  • Funnel on macOS requires the open-source Tailscale app variant.

Learn more