#hetzner vps failed with seve
1 messages · Page 1 of 1 (latest)
That error only happens if the Gateway is not actually bound to loopback when Tailscale Serve/Funnel is enabled.
In your snippet I see two likely issues:
-
Typo / wrong key: you wrote
gatewat(notgateway). If that’s really in your config file, OpenClaw will ignore it andgateway.bindwill stay whatever it was before. -
Config not being applied (common when running as a service): you might be editing a different config than the one the gateway service uses.
Correct config (JSON5) should look like this:
{
gateway: {
bind: "loopback",
tailscale: { mode: "serve" },
},
}
Docs: https://docs.openclaw.ai/gateway/tailscale
Quick checks
Please run and paste:
openclaw --version
openclaw gateway status
openclaw config get gateway.bind
openclaw config get gateway.tailscale.mode
openclaw gateway status will also tell you Config (service) vs Config (cli) — if those differ, you’re editing the wrong file/profile.
After fixing the key + config file, restart:
openclaw gateway restart
Yes — Docker is very likely the reason you still see that error even though your config says bind: "loopback".
In the common Docker/Compose setups (including our VPS guides), the container is started with a command like:
command: ["...","gateway","--bind","${OPENCLAW_GATEWAY_BIND}", ...]
That CLI --bind overrides whatever is in openclaw.json.
So if your .env has e.g.:
OPENCLAW_GATEWAY_BIND=lan
# or tailnet
you’ll get exactly:
tailscale serve/funnel requires gateway bind=loopback (127.0.0.1)
Fix
- Set
OPENCLAW_GATEWAY_BIND=loopbackin the.envused by Docker Compose OR - Remove the
--bind ...part from the containercommandand rely onopenclaw.json.
One more Docker-specific gotcha (important)
gateway.tailscale.mode: "serve" only works if the container can run tailscale serve against a tailscaled daemon it can reach.
Simplest working patterns are either:
- Run OpenClaw on the host (no Docker) and use integrated Tailscale Serve, or
- Keep OpenClaw in Docker, but run Tailscale Serve on the host (manual), proxying to
127.0.0.1:18789(port-mapped container). In that case, setgateway.tailscale.mode: "off"in OpenClaw.
If you paste your docker-compose.yml command: section and your .env values for OPENCLAW_GATEWAY_BIND + OPENCLAW_GATEWAY_PORT, I can tell you exactly which line is overriding it.
If Tailscale is running on the VPS host (outside Docker), then don’t use OpenClaw’s integrated gateway.tailscale.mode: "serve" inside the container (it expects to be able to run tailscale serve and see the connection as loopback). Instead:
1) First confirm the container is reachable on the VPS itself
On the VPS host, this must work:
curl -v http://127.0.0.1:18789/ | head
If that fails, it’s a Docker port mapping problem (you need something like 127.0.0.1:18789:18789).
2) Configure Tailscale Serve on the VPS host (correct syntax)
Try (on the VPS host):
sudo tailscale serve --bg localhost:18789
sudo tailscale serve status
Then from a device that is connected to the same tailnet, open:
https://<your-vps-hostname>.<your-tailnet>.ts.net/(no:18789)
Common “it doesn’t work” cause: people try the MagicDNS URL from a device not on Tailscale, but Serve is tailnet-only.
3) If the page opens but OpenClaw shows “unauthorized” / keeps reconnecting
That means Serve is fine, but the Gateway token auth isn’t set in the Control UI.
Get the token on the gateway host (inside the container if needed) and paste it into the dashboard settings:
openclaw config get gateway.auth.token
4) Avoid fighting two “Serve” systems
If you’re doing Serve manually on the host, set in openclaw.json:
{ gateway: { tailscale: { mode: "off" } } }
If you paste:
sudo tailscale serve statusoutput, and- what you see when opening the MagicDNS URL (connection refused vs unauthorized vs blank),
I can tell which of the above is the blocker.
You can, but only if the container can talk to a tailscaled daemon in the same network namespace.
Right now you have tailscale on the VPS host and OpenClaw in Docker. In that setup, gateway.tailscale.mode: "serve" inside OpenClaw is basically the wrong tool: OpenClaw tries to run tailscale serve itself, and it expects the backend connection to look like it’s coming from 127.0.0.1 (loopback). Docker NAT breaks that assumption.
Recommended (simple + reliable)
Keep Tailscale on the host and run Serve there, manually (or via systemd), and set:
{ gateway: { tailscale: { mode: "off" } } }
This keeps concerns separated: Docker runs OpenClaw; host tailscaled runs Serve.
If you really want “automation from Docker”
You need to move Tailscale into the Docker stack (or share host networking/socket), e.g. one of these patterns:
Option A: run OpenClaw with host networking (Linux VPS only)
network_mode: "host"for the OpenClaw container- then host
tailscale servecan genuinely reach it on127.0.0.1:18789 - (and OpenClaw can see loopback correctly)
Option B: Tailscale sidecar container + shared network namespace
- Run a
tailscale/tailscalecontainer that runstailscaled - Run OpenClaw with
network_mode: "service:tailscale"so they share the same network stack - Then run
tailscale serve …inside that same namespace
This is doable, but it’s more moving parts (auth key, /dev/net/tun, NET_ADMIN capability, persisting tailscale state, etc.).
If you tell me which approach you prefer (host networking vs tailscale sidecar), and paste your current docker-compose.yml, I’ll point out the exact changes needed for that path.
That curl result is the smoking gun.
* Recv failure: Connection reset by peer on 127.0.0.1:18789 usually means:
- Docker port-forward is reaching the container IP (e.g.
172.x.x.x:18789), but - your gateway process inside the container is bound to 127.0.0.1 only (
bind: "loopback"), - so the container kernel rejects the forwarded connection → RST (“reset by peer”).
What to do (Docker + Tailscale on the host)
If Tailscale is on the VPS host (not in the container), the clean setup is:
- Disable OpenClaw’s integrated tailscale mode in
openclaw.json(because it forces loopback bind):
{
gateway: {
tailscale: { mode: "off" },
bind: "lan", // important in Docker
port: 18789,
},
}
- Keep Docker exposure safe by mapping only to host loopback:
ports:
- "127.0.0.1:18789:18789"
- Restart the container, then verify on the VPS host:
curl -v http://127.0.0.1:18789/
This should return HTML (not reset).
- Now run Serve on the VPS host:
sudo tailscale serve --bg localhost:18789
sudo tailscale serve status
Then open https://<magicdns>/ from a Tailscale-connected device.
Important note about auth
With Docker NAT, OpenClaw usually won’t accept the “Tailscale identity header” tokenless flow, because the backend request won’t look like it came from 127.0.0.1 inside the container. So expect to still paste the gateway token in the Control UI.
If you want the fully “integrated”/tokenless Serve flow, you typically need host networking for the OpenClaw container (or run tailscaled inside Docker).
That error is expected once you set OPENCLAW_GATEWAY_BIND=lan.
When the Gateway is non-loopback (bind: "lan" / 0.0.0.0), OpenClaw requires you to explicitly allow which browser Origins are allowed to open the Control UI WebSocket (CSRF / drive‑by defense).
Fix (recommended): set gateway.controlUi.allowedOrigins
In your openclaw.json add something like:
{
gateway: {
bind: "lan",
tailscale: { mode: "off" }, // if you're doing tailscale serve on the HOST
controlUi: {
allowedOrigins: [
"http://127.0.0.1:18789",
"http://localhost:18789",
"https://YOUR-VPS-NAME.YOUR-TAILNET.ts.net"
],
},
},
}
Notes:
- Origins must match exactly what your browser uses (scheme + host + port).
- For Tailscale Serve, it’s typically
https://<magicdns>(no port).
Then restart the gateway container.
Alternative (works, but less safe)
You can set: