Add Remote Nodes to OpenClaw via Tailscale

Author
Lukas von Kunhardt
Published
February 9, 2026
Tags
Essentials
Prerequisites

OpenClaw on its own runs on one server. With remote nodes, it can reach into your Mac, your other VPSes, any machine on your Tailscale network, and actually do things there.

I'm on the train, checking WhatsApp, and I realize I need a spreadsheet that's on my MacBook at home. Not in iCloud. Not synced anywhere. Just sitting in a folder. I text OpenClaw: find me that spreadsheet. It reaches into my Mac, finds the file, and sends back the data. I tell it to punch the numbers on it. It does. Both things: finding the file and doing the calculations, from my phone, while my MacBook is in standby at home.

That's what made the personal assistant workflow feel real for the first time. Not because the AI got smarter, but because it could finally reach my stuff.

The first guide got OpenClaw running on a server with WhatsApp connected. That gives you an always-on AI you can text from your phone. But it only has access to one machine. This guide connects it to everything else: your Mac, your other servers, any machine on your Tailscale network.

How Nodes Work

OpenClaw uses a hub-and-spoke architecture. The gateway is the hub: it runs on your server, handles WhatsApp messages, and talks to the AI model. Nodes are the spokes: lightweight processes running on other machines that connect back to the gateway over WebSocket.

When the AI needs to run a command on your Mac, the gateway sends the request to your Mac's node process. The node executes it and sends the result back. When a command needs your approval, the confirmation gets forwarded to your WhatsApp. That approval flow, where you okay commands from your phone, is only possible through proper node hosts. It's the reason OpenClaw recommends this approach over SSH tunneling from the gateway container.

Each node exposes system capabilities and optionally browser access. You control exactly which commands are allowed through an exec-approvals config.

I have three nodes connected right now:

  • My MacBook, where my files and projects live. This is the one I use most.
  • Tiro, a Hetzner VPS running n8n, Supabase, and a monitoring stack.
  • Vacura, another VPS running a worker process, NocoDB, and Postgres.

The Mac connection is probably the most useful one for most people. OpenClaw is always on because it's running on the server. So even when I'm away from my desk, I can ask it to do things on my laptop through WhatsApp. A Mac Mini is becoming more and more attractive for this, because the MacBook needs to be awake. It does seem to work in standby, though. Just not when it's fully off.

What You Need

  1. A working OpenClaw gateway from the first guide
  2. Tailscale on the gateway server and every machine you want to connect
  3. The machine(s) you want to add as nodes

Fair Warning About Time

This took me multiple hours, even with Claude Code doing the debugging and being fairly comfortable with servers and Docker. I wanted to do it the stable way so I wouldn't have to rebuild everything a month later when something crashes. This guide documents every gotcha I hit so you can hopefully get through it in about an hour. Most of that time is waiting for things to install and doing the Tailscale setup, which is a bit manual.

Step 1: Make the Gateway Reachable on Tailscale

Your OpenClaw gateway is inside Docker, bound to 127.0.0.1:18789. Only the server itself can reach it. Remote nodes need to connect to it over your Tailscale network.

Find your gateway server's Tailscale IP:

tailscale ip -4

Then update your docker-compose.yml to also bind the gateway port to the Tailscale IP:

ports:
  # Loopback for local access (already there)
  - "127.0.0.1:${OPENCLAW_GATEWAY_PORT}:18789"
  # Tailscale IP for remote nodes (add this)
  - "100.x.y.z:${OPENCLAW_GATEWAY_PORT}:18789"

Replace 100.x.y.z with your actual Tailscale IP (run tailscale ip -4 to find it).

Restart the container:

docker compose down && docker compose up -d

If docker compose doesn't work, try docker-compose with a hyphen. Older Docker installs only have the v1 syntax.

Verify it's listening on both addresses:

ss -ltnp | grep 18789

You should see two entries: one for 127.0.0.1 and one for your Tailscale IP.

Step 2: Allow Traffic Through Tailscale's Firewall

This was the biggest time sink for me. Even after exposing the port, my other machines got "Connection refused" when trying to reach the gateway.

The problem: Tailscale has its own firewall chain in iptables that drops inbound tailnet traffic by default. If you check with iptables -L ts-input -n on the gateway server, you'll see a DROP rule for the entire 100.64.0.0/10 range (all Tailscale IPs).

Don't add a manual iptables rule. It works but disappears on reboot.

The right fix is a Tailscale Grant. Go to admin.tailscale.com, then Access Controls, then Grants, and add:

{
  "src": ["autogroup:member"],
  "dst": ["100.x.y.z"],
  "ip": ["tcp:18789"]
}

Use your gateway's Tailscale IP as the destination.

Three things I learned the hard way:

Grants don't accept hostnames. I tried using the server's Tailscale hostname and got "invalid address." You need the IP.

Tailscale ACLs are legacy. If you see old documentation about ACL rules, ignore it. Grants are the newer system.

iptables won't reflect the change. After saving the grant, the DROP rule still showed up in iptables -L ts-input. I thought it didn't work. But grants operate at the WireGuard layer, not the iptables layer. The display is misleading. Test with curl from another machine on your Tailscale network instead:

curl http://100.x.y.z:18789/

If you get any response (even an error page), the connection is working.

Step 3: Install Node.js and OpenClaw on the Remote Machine

SSH into the machine you want to connect. OpenClaw requires Node.js 22 or later.

curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
apt-get install -y nodejs

If the server has an older Node.js, this upgrades it. On one of my servers, upgrading from Node 18 removed about 134 old packages. It looks alarming in the terminal output but doesn't break anything.

Then install OpenClaw:

npm install -g openclaw@latest

Verify:

node --version
openclaw --version

Tip: If your servers have auto-generated Tailscale hostnames (like docker-ce-ubuntu-4gb-nbg1-1), you can rename them to something readable with tailscale set --hostname=tiro. Makes the node list much cleaner.

Step 4: Set Up the Node as a systemd Service

This is the fiddly part. There are three ways to start a node, and only one of them works reliably on Linux.

Option A: nohup openclaw node run in the background. Doesn't work. Here's why: when a node connects to the gateway for the first time, the gateway responds with "pairing required" and the node exits immediately. The gateway auto-approves the pairing about 2 seconds later, but by then the node process is already dead. You'd have to manually restart it after every first connection.

Option B: openclaw node install (the built-in systemd installer). Also broken as of February 2026. It has a bug where it tries to create a service file called openclaw-gateway.service instead of openclaw-node.service and fails.

Option C: Create the systemd service yourself. This is what works.

Create /etc/systemd/system/openclaw-node.service:

[Unit]
Description=OpenClaw Node Host
After=network-online.target tailscaled.service
Wants=network-online.target

[Service]
Type=simple
Environment=OPENCLAW_GATEWAY_TOKEN=<your-gateway-token>
ExecStart=/usr/bin/openclaw node run --host <gateway-tailscale-ip> --port 18789 --display-name "<name>"
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Replace the placeholders with your actual values. The display name is what shows up in the gateway's node list.

Enable and start:

systemctl daemon-reload
systemctl enable openclaw-node
systemctl start openclaw-node

The Restart=always with RestartSec=5 is the trick that makes pairing work:

  1. Node connects, gets "pairing required," exits
  2. Gateway auto-approves the pairing in the background
  3. systemd waits 5 seconds, restarts the node
  4. Node reconnects with the approved identity, stays connected

After about 15 seconds, check:

systemctl status openclaw-node

Should show active (running). On the gateway server:

openclaw nodes status

Your new node should appear as paired · connected.

Repeat for every server you want to add.

Step 5: Configure Exec-Approvals

A connected node will run any command the AI sends it unless you restrict it. You want an allowlist.

Create ~/.openclaw/exec-approvals.json on each node:

{
  "version": 1,
  "defaults": {
    "security": "allowlist",
    "ask": "on-miss",
    "askFallback": "deny"
  }
}
  • allowlist: only whitelisted commands run
  • on-miss: if a command isn't in the list, OpenClaw asks you to approve it through WhatsApp
  • deny: if you're not available to approve, the command gets denied

With on-miss mode, you build up the allowlist naturally. The AI tries to run something, you get a WhatsApp message asking to approve, and approved commands get remembered for next time.

For a server running Docker, a reasonable starting set: docker, docker-compose, systemctl, journalctl, ls, cat, find, mkdir, cp, mv, df, du, free, uptime, ps, ss, ip, hostname, git, npm, node, curl, tailscale.

One thing to know: the allowlist operates at the binary path level, not the argument level. Allowing docker permits docker ps but also docker rm. For finer control, OpenClaw has a sandboxing system that runs commands in isolated containers. For my setup, the allowlist is enough.

Exec-approvals docs

Connecting Your Mac

If you followed the first guide, your Mac might already be connected as a node. The same approach applies, but macOS uses launchd instead of systemd.

npm install -g openclaw@latest
openclaw node install --host <gateway-tailscale-ip> --port 18789 --display-name "My Mac"

Good news: openclaw node install actually works on macOS. It's only the Linux version that has the service name bug.

The Mac connection seems to work in standby, at least in my experience. But not when the Mac is fully powered off. If you want an always-on Mac node, a Mac Mini tucked in a corner is the move.

Known Issues

IssueWhat to do
Tailscale grant destinations don't accept hostnamesUse the Tailscale IP (100.x.x.x), not the machine name
iptables -L ts-input doesn't show grantsGrants work at the WireGuard layer. Test with curl instead
openclaw node install creates wrong service file on LinuxCreate the systemd unit manually (Step 4)
Node exits before pairing completesRestart=always in the systemd unit handles this automatically
docker compose vs docker-composeOlder Docker installs only have v1 (hyphenated). Check which you have
Node 18 to 22 upgrade removes packagesNormal on Ubuntu via NodeSource. Looks scary, doesn't break anything

Quick Reference

For when you've done this before and just need the commands:

  1. Tailscale grant: admin.tailscale.com, Access Controls, Grants: src: autogroup:member, dst: <gateway-ip>, ip: tcp:18789
  2. docker-compose.yml: add "<tailscale-ip>:${OPENCLAW_GATEWAY_PORT}:18789" to ports
  3. On each remote server:
curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
apt-get install -y nodejs
npm install -g openclaw@latest
  1. Create systemd service at /etc/systemd/system/openclaw-node.service (Step 4)
  2. Create ~/.openclaw/exec-approvals.json with allowlist mode
  3. Start: systemctl enable --now openclaw-node (fails once for pairing, auto-recovers on restart)

What's Next

With nodes connected, OpenClaw becomes genuinely useful for work. Text it from WhatsApp and have it check Docker containers on one server, pull a file from your Mac, or restart a service somewhere else.

The next guides in this series will cover making OpenClaw smarter: adding skills, setting up heartbeats and scheduled tasks, and building workflows where it handles things you'd otherwise forget about.

Resources