Setting Up OpenClaw Node Across 2 VPS via Tailscale


Context

We have 2 VPS instances:

  • VPS 1 (Master): Running OpenClaw Gateway on WSL2.
  • VPS 2 (Node/Slave): Ubuntu Server, needs to run OpenClaw Node for remote execution.
  • Network: Both connected via Tailscale VPN.

Architecture

To ensure security without exposing ports to the public internet, we utilize Tailscale Serve on the Master to create a secure HTTPS tunnel.

  • Master (VPS 1): Runs Gateway, bound to loopback, using Tailscale Serve to expose it to the Tailnet as HTTPS.
  • Node (VPS 2): Runs OpenClaw Node Service, connecting to Master via Tailscale’s MagicDNS URL.

Part 1: Configuring VPS Master (Gateway)

1. OpenClaw Gateway Config

Edit ~/.openclaw/openclaw.json:

{
  "gateway": {
    "bind": "loopback",  // Listen only on 127.0.0.1 (Secure)
    "port": 18789,
    "tailscale": {
      "mode": "serve"    // Auto-enable Tailscale Serve HTTPS
    },
    // ... other configs
  }
}

2. Restart and Get URL

Restart Gateway:

openclaw gateway restart

Check MagicDNS URL:

tailscale serve status
# Result: https://vps1-name.tailnet-name.ts.net

(Save this URL for VPS 2 setup)


Part 2: Configuring VPS Node (Slave)

This is the tricky part, often plagued by config caching or systemd ignoring environment variables.

1. Install OpenClaw CLI

npm install -g openclaw

2. Fixing “Stubborn 127.0.0.1 Connection”

In practice, the Node process often prioritizes old config files or defaults to localhost. The robust solution is to Hard-code parameters into the Systemd Service instead of relying on JSON files.

Step 1: Stop old service and clean up config files:

systemctl --user stop openclaw-node.service
rm ~/.openclaw/node.json           # Critical: This file causes cache issues
rm ~/.openclaw/openclaw.json       # Remove to avoid conflicts

Step 2: Create a “Force Remote” Systemd Service: Create ~/.config/systemd/user/openclaw-node.service with:

[Unit]
Description=OpenClaw Node Host (Remote)
After=network-online.target
Wants=network-online.target

[Service]
# Critical: Use CLI flags to override all configs
ExecStart=/usr/bin/node /usr/lib/node_modules/openclaw/dist/index.js node run --host "vps1-name.tailnet-name.ts.net" --port 443 --tls

# Environment variables
Environment=HOME=/home/username
Environment="PATH=/usr/bin:/usr/local/bin:/bin"
# Token must be passed via ENV (CLI does not support --token flag)
Environment="OPENCLAW_GATEWAY_TOKEN=YOUR_MASTER_TOKEN_HERE"
# Fix SSL issues if Node rejects Tailscale's Let's Encrypt cert
Environment="NODE_TLS_REJECT_UNAUTHORIZED=0"

Restart=always
RestartSec=5
KillMode=process

[Install]
WantedBy=default.target

(Replace vps1-name... and YOUR_MASTER_TOKEN_HERE with actual values)

Step 3: Enable and Start Service

systemctl --user daemon-reload
systemctl --user enable openclaw-node.service
systemctl --user restart openclaw-node.service

Part 3: Pairing

Once the Node starts, it sends a pairing request to the Master.

  1. On VPS 1 (Master): Check pending list.

    openclaw devices list

    You should see a device with Role: node in Pending state.

  2. Approve Device:

    openclaw devices approve <REQUEST_ID>
  3. Completion: After approval, VPS 2 will auto-reconnect and switch to Connected status. Verify on Master: openclaw nodes status

Troubleshooting Lessons

  1. Persistent ECONNREFUSED 127.0.0.1:

    • Usually caused by a leftover ~/.openclaw/node.json. Delete it immediately.
    • Or the initial openclaw node install command generated a service file with hardcoded --host 127.0.0.1.
  2. unknown option '--token' Error:

    • openclaw node run does not accept --token. You must use the OPENCLAW_GATEWAY_TOKEN environment variable.
  3. SSL/TLS Connection Errors:

    • When using Tailscale Serve, ensure you add --tls flag and use port 443.
    • If certificate errors persist, set NODE_TLS_REJECT_UNAUTHORIZED=0.

Documented by SensaKai Team - 2026