Tailscale Serve and Funnel Integration for Gateway Dashboard
This page explains how OpenClaw automates Tailscale Serve or Funnel to expose the Gateway dashboard securely. It covers configuration modes and examples for tailnet-only or public HTTPS access.
Read this when
- Exposing the Gateway Control UI outside localhost
- Automating tailnet or public dashboard access
OpenClaw can automatically manage Tailscale Serve (tailnet only) or Funnel (public internet) for the Gateway dashboard and its WebSocket port. This arrangement binds the gateway to loopback while Tailscale handles HTTPS, routing, and, when using Serve, identity headers.
Modes
gateway.tailscale.mode:
| Mode | Behavior |
|---|---|
serve | Tailnet-only access through Serve via tailscale serve. The gateway remains on 127.0.0.1. |
funnel | Public HTTPS access through Funnel via tailscale funnel. A shared password is required. |
off (default) | Tailscale automation is not active. |
Status and audit output refer to Tailscale exposure to indicate this OpenClaw Serve or Funnel mode. off signals that OpenClaw is not actively managing Serve or Funnel; it does not imply the local Tailscale daemon is stopped or signed out.
Config examples
Tailnet-only (Serve)
{
gateway: {
bind: "loopback",
tailscale: { mode: "serve" },
},
}
Open: https://<magicdns>/ (or your configured gateway.controlUi.basePath)
To present the Control UI through a named Tailscale Service rather than the device hostname, assign gateway.tailscale.serviceName to the Service name:
{
gateway: {
bind: "loopback",
tailscale: { mode: "serve", serviceName: "svc:openclaw" },
},
}
On startup, the Service URL appears as https://openclaw.<tailnet-name>.ts.net/ instead of the device hostname. Tailscale Services require the host to be an approved tagged node in your tailnet. Configure the tag and approve the Service in Tailscale before enabling this; otherwise tailscale serve --service=... fails during gateway startup.
Tailnet-only (bind to Tailnet IP)
Use this to make the gateway listen directly on the Tailnet IP, bypassing Serve or Funnel:
{
gateway: {
bind: "tailnet",
auth: { mode: "token", token: "your-token" },
},
}
Connect from another device on the tailnet:
- Control UI:
http://<tailscale-ip>:18789/ - WebSocket:
ws://<tailscale-ip>:18789
Note
When a bindable Tailnet IPv4 address exists, the Gateway also requires
http://127.0.0.1:18789for authenticated clients on the same host. If no Tailnet address is available at startup, it falls back to loopback only. Restart after Tailscale becomes available to add direct Tailnet access. Neither method exposes the gateway to the LAN or the public internet.
Public internet (Funnel + shared password)
{
gateway: {
bind: "loopback",
tailscale: { mode: "funnel" },
auth: { mode: "password", password: "replace-me" },
},
}
Using OPENCLAW_GATEWAY_PASSWORD is preferred over storing a password on disk.
CLI examples
openclaw gateway --tailscale serve
openclaw gateway --tailscale funnel --auth password
Auth
gateway.auth.mode governs the handshake:
| Mode | Use case |
|---|---|
none | Only private ingress |
token (default when OPENCLAW_GATEWAY_TOKEN is set) | Shared token |
password | Shared secret through OPENCLAW_GATEWAY_PASSWORD or config |
trusted-proxy | Identity-aware reverse proxy; see Trusted Proxy Auth |
Tailscale identity headers (Serve only)
When tailscale.mode: "serve" and gateway.auth.allowTailscale is true, authentication for the Control UI and WebSocket can rely on Tailscale identity headers (tailscale-user-login) instead of a token or password. OpenClaw validates the header by resolving the request's x-forwarded-for address through the local Tailscale daemon (tailscale whois) and matching it to the header login before accepting the request. A request qualifies for this path only when it arrives from loopback and includes Tailscale's x-forwarded-for, x-forwarded-proto, and x-forwarded-host headers.
This tokenless flow assumes the gateway host is trusted. If untrusted local code may run on the same host, set gateway.auth.allowTailscale: false and require token or password authentication instead.
Scope of the bypass:
- Applies only to the Control UI WebSocket authentication surface. HTTP API endpoints (
/v1/*,/tools/invoke,/api/channels/*, etc.) never use Tailscale identity header authentication; they always follow the gateway's normal HTTP auth mode. - For Control UI operator sessions that already carry browser device identity, a verified Tailscale identity skips the bootstrap token or QR pairing round trip.
- It does not bypass device identity itself. Device-less clients are still rejected, and node role connections still go through normal pairing and authentication checks.
Notes
- The
tailscalecommand line interface must be installed and authenticated for Tailscale Serve/Funnel. - To prevent exposure to the internet,
tailscale.mode: "funnel"will not start unless the authentication mode is set topassword. gateway.tailscale.serviceNameis specific to Serve mode and gets forwarded totailscale serve --service=<name>. Its value must follow Tailscale'ssvc:<dns-label>syntax, for instancesvc:openclaw. Service hosts in Tailscale are required to be tagged nodes, and the Service might need approval from the admin console before Serve can make it available.- When shutting down,
gateway.tailscale.resetOnExitremoves anytailscale serve/tailscale funnelconfiguration that was applied. gateway.tailscale.preserveFunnel: truemaintains atailscale funnelroute configured externally, surviving gateway restarts. Withmode: "serve", OpenClaw inspectstailscale funnel statusbefore reapplying Serve and skips it if a Funnel route already occupies the gateway port. The existing password-only policy for OpenClaw managed Funnel stays the same.- When a Tailnet IPv4 address is available,
gateway.bind: "tailnet"uses a direct Tailnet bind (without HTTPS, Serve, or Funnel) along with a mandatory local127.0.0.1; otherwise it falls back to loopback only. gateway.bind: "auto"gives priority to loopback; to restrict network visibility to the Tailnet while still allowing loopback access on the same host, usetailnet.- Only the Gateway control UI and WS are exposed through Serve/Funnel. Nodes connect via the same Gateway WS endpoint, so Serve also provides access for nodes.
Tailscale prerequisites and limits
- Your tailnet must have HTTPS enabled for Serve to work; the CLI will prompt you if it is not active.
- Serve adds Tailscale identity headers to requests; Funnel does not.
- Funnel requires Tailscale version 1.38.3 or newer, MagicDNS, HTTPS enabled, and a funnel node attribute.
- Funnel supports only ports
443,8443, and10000over TLS. - On macOS, the open source Tailscale app variant is required for Funnel.
Browser control (remote Gateway + local browser)
If you want the Gateway on one machine but control it from a browser on another, set up a node host on the browser machine and ensure both are on the same tailnet. The Gateway forwards browser actions to the node; no separate control server or Serve URL is needed.
Do not use Funnel for browser control; treat node pairing the same as operator access.
Learn more
- Overview of Tailscale Serve: https://tailscale.com/kb/1312/serve
tailscale servecommand documentation: https://tailscale.com/kb/1242/tailscale-serve- Overview of Tailscale Funnel: https://tailscale.com/kb/1223/tailscale-funnel
tailscale funnelcommand documentation: https://tailscale.com/kb/1311/tailscale-funnel