Updated: 2026-02-26 (GMT+7) — Added Gateway config for running behind Tailscale Serve (
gateway.trustedProxies), clarified correct pairing flow viaopenclaw devices ..., and improved systemd/token notes.
Context
We have two servers:
- VPS 1 (Master / Gateway host): runs OpenClaw Gateway.
- VPS 2 (Node / Worker): Ubuntu Server that runs a headless OpenClaw Node Host for remote execution.
- Network: both servers are connected via Tailscale (same tailnet).
Recommended architecture
To avoid exposing public ports, we use Tailscale Serve on the Master:
- Master: Gateway binds to loopback (
127.0.0.1) for safety, then Tailscale Serve exposes the Control UI + WebSocket over HTTPS/WSS. - Node: runs an OpenClaw Node Host (systemd user service) and connects to Master via a MagicDNS Serve URL over TLS (port
443).
Example placeholder URL (not a real host):
https://gateway-master.tailnet-xyz.ts.net
Part 1: Configure the Master (Gateway)
1) OpenClaw Gateway config
Edit ~/.openclaw/openclaw.json on the Master:
{
gateway: {
bind: "loopback", // Safety: listen on localhost only
port: 18789,
// Important when running behind a reverse proxy (Tailscale Serve -> localhost)
trustedProxies: ["127.0.0.1", "::1"],
auth: {
mode: "token",
token: "YOUR_GATEWAY_TOKEN"
},
tailscale: {
mode: "serve",
resetOnExit: false
},
// Optional: tighten origins for Control UI
controlUi: {
allowedOrigins: ["https://gateway-master.tailnet-xyz.ts.net"]
}
}
}
Why trustedProxies?
With tailscale serve, requests reach the Gateway via a local proxy (127.0.0.1) plus X-Forwarded-* headers. Trusting loopback proxy IPs avoids warnings like “Proxy headers detected from untrusted address…” and helps keep proxied client detection/pairing behavior consistent.
2) Restart Gateway + get Serve URL
openclaw gateway restart
tailscale serve status
# Example: https://gateway-master.tailnet-xyz.ts.net
Part 2: Configure the Node (Worker)
This is the part where stale state/config often causes the node to stubbornly connect to 127.0.0.1.
1) Install OpenClaw CLI
npm install -g openclaw
2) Create a systemd user service (recommended)
We recommend hard-coding the remote gateway connection parameters in the unit file to override any old config/state.
Create/edit: ~/.config/systemd/user/openclaw-node.service
[Unit]
Description=OpenClaw Node Host (Remote)
After=network-online.target
Wants=network-online.target
[Service]
# Force remote gateway via Tailscale Serve (WSS)
ExecStart=/usr/bin/node /usr/lib/node_modules/openclaw/dist/index.js node run --host "gateway-master.tailnet-xyz.ts.net" --port 443 --tls --display-name "Worker-Node-1"
Restart=always
RestartSec=5
KillMode=process
Environment=HOME=/home/username
Environment="PATH=/usr/bin:/usr/local/bin:/bin"
# Token must be provided via ENV
# IMPORTANT: do NOT wrap token with extra quotes like 'TOKEN'
Environment="OPENCLAW_GATEWAY_TOKEN=YOUR_GATEWAY_TOKEN"
# Not recommended by default (insecure; disables TLS verification)
# Environment="NODE_TLS_REJECT_UNAUTHORIZED=0"
[Install]
WantedBy=default.target
Enable and start:
systemctl --user daemon-reload
systemctl --user enable --now openclaw-node.service
systemctl --user status openclaw-node.service --no-pager
Part 3: Pairing (Approve the node)
Right after the node starts, you will typically see:
disconnected (1008): pairing required
This is expected: a new node must be approved once on the Gateway.
1) List pending requests (on Master)
openclaw devices list
You should see Pending (1) with Role: node.
Note: this flow is approved via Devices CLI (
openclaw devices ...), notopenclaw nodes pending/approve.
2) Approve pairing
Approve latest:
openclaw devices approve --latest
Or approve by requestId:
openclaw devices approve <REQUEST_ID>
Verify:
openclaw devices list
3) Restart node + confirm connected
On the Worker:
systemctl --user restart openclaw-node
journalctl --user -u openclaw-node.service -n 80 --no-pager
On the Master:
openclaw nodes status --connected
4) End-to-end test
On the Master:
openclaw nodes run --node "Worker-Node-1" --raw "uname -a && whoami && pwd"
Troubleshooting (field notes)
- Node keeps connecting to
127.0.0.1:18789
- Usually caused by stale state/config.
- Durable fix: hard-code
--host ... --port 443 --tlsin the systemd unit.
- Token mismatch caused by systemd quoting
- Wrong (token includes quotes):
Environment="OPENCLAW_GATEWAY_TOKEN='TOKEN'"
- Correct:
Environment="OPENCLAW_GATEWAY_TOKEN=TOKEN"
- Avoid
NODE_TLS_REJECT_UNAUTHORIZED=0
- Not recommended by default (disables TLS verification).
- Use only as a last-resort debugging step and remove afterwards.
- Do not run a Gateway on the Worker VPS
- The Worker should run only the node host (
openclaw-node.service). - Running a local gateway on the Worker can confuse CLI/status and lead to localhost mis-targeting.
Notes by SensaKai Team — updated 2026-02-26.