#tailscale setup

1 messages · Page 1 of 1 (latest)

faint gardenBOT
#

To help others find answers, you can mark your question as solved via Right click solution message -> Apps -> âś… Mark Solution

devout swanBOT
#

It was basically 2 separate “this looks fine until it doesn’t” issues:

1) Gateway crash: invalid BlueBubbles config keys

In the config, channels.bluebubbles had host + port fields. Those aren’t valid keys for the BlueBubbles channel, so the gateway would die during config/schema validation.

Fix was: remove host/port and use the supported serverUrl field instead.

Correct shape (from docs):

{
  channels: {
    bluebubbles: {
      enabled: true,
      serverUrl: "http://192.168.1.100:1234",
      password: "example-password",
#
      webhookPath: "/bluebubbles-webhook",
    },
  },
}

You can catch this instantly with:

openclaw config validate

Docs: https://docs.openclaw.ai/channels/bluebubbles

2) “Unauthorized” / token mismatch: launchd env vs config

The macOS launchd service had OPENCLAW_GATEWAY_TOKEN set to one value, but gateway.auth.token in ~/.openclaw/openclaw.json was different.

slim isle
#

sorry, you didn't get the query, i believe...

I want to access control ui from my laptop .. gateway is setup on digital ocean droplet... help me setup tailscale... i did ran some commands and i beleive the device was added to my tailscale account, but not sure after that

devout swanBOT
#

To access the Control UI from your laptop when the Gateway runs on a DigitalOcean droplet, you want both machines on the same Tailnet, then expose the Gateway either via Tailscale Serve (recommended) or by binding the Gateway directly to the Tailnet IP.

Before anything: on the droplet, paste this:

openclaw --version
openclaw gateway status
tailscale status
tailscale ip -4

That tells us: your OpenClaw version, whether the gateway is healthy, and what Tailscale thinks your droplet’s Tailnet IP/name is.


Option A (recommended): Tailscale Serve (HTTPS, gateway stays on 127.0.0.1)

This is the cleanest setup: OpenClaw binds to loopback, and Tailscale provides the HTTPS URL on your tailnet.

#
  1. On the droplet, set:
openclaw config set gateway.tailscale.mode "serve"
openclaw config set gateway.bind "loopback"
openclaw gateway restart
  1. On your laptop (also logged into the same Tailnet), open:
  • https://<your-droplet-magicdns>/

You can find the MagicDNS name in tailscale status.

  1. If the UI asks for auth, get the token from the droplet:
#
openclaw config get gateway.auth.token

…and paste it into the Control UI’s “Gateway Token” field.

Docs: https://docs.openclaw.ai/gateway/tailscale


Option B: Bind gateway directly to Tailnet IP (no Serve, plain HTTP)

If you don’t want Serve:

  1. On the droplet:
#
openclaw config set gateway.bind "tailnet"
openclaw gateway restart
  1. Then from your laptop open:
  • http://<droplet-tailscale-ip>:18789/

(The port is usually 18789; openclaw gateway status will confirm.)

Docs: https://docs.openclaw.ai/gateway/tailscale (see “bind to Tailnet IP”)


Tell me what you did so far

When you say “I ran some commands and the device was added” — can you paste:

#
  • the exact commands you ran (even partial history is fine)
  • output of tailscale status from the droplet

Then I can tell you exactly which option you’re closest to and what’s missing.

slim isle
#

do i need to install tailnet on mac? can't i just access it via browser, without downloading tailnet on my laptop


tailnetscal status and ip -4 showed me the ip and the host name,
100...** ubuntu-****** a***d@ linux -

devout swanBOT
#

That 100.x.y.z IP you’re seeing is a Tailscale (CGNAT) address. It’s only routable from inside your tailnet.

So if your laptop is not running Tailscale (or otherwise connected to the tailnet via a router/proxy), your browser on the laptop simply can’t reach:

  • http://100.x.y.z:18789/ or
  • https://ubuntu-… (MagicDNS)

Do you need to “install tailnet” on macOS?

You need to join the tailnet somehow. The normal way is: install the Tailscale app on the Mac and log in. There isn’t a “browser-only” way to be on the tailnet.


If you really don’t want Tailscale on the laptop, you have 2 alternatives

1) Make it public with Tailscale Funnel (not my first choice)

This makes the Control UI reachable from the public internet, so you must use a strong password.

#

Config (on droplet):

{
  gateway: {
    bind: "loopback",
    tailscale: { mode: "funnel" },
    auth: { mode: "password", password: "replace-with-strong-password" },
  },
}

Then restart the gateway.

Docs: https://docs.openclaw.ai/gateway/tailscale (Funnel section)

2) Use an SSH tunnel (private, no Tailscale needed on laptop)

#

From your laptop:

ssh -L 18789:127.0.0.1:18789 ubuntu@<droplet-public-ip>

Then open on your laptop:

  • http://127.0.0.1:18789/

(You still authenticate with your gateway token/password as usual.)


If you tell me which of the 3 you want (Serve + Tailscale on Mac / Funnel / SSH tunnel), I’ll give you the exact minimal steps for that path.

slim isle
#

installed on mac.. can see mac ip and ubuntu ip on tailscale status... how to access control ui now.. the link https://100.<ubuntu_ip>:18789/ not working.. tried http as wel

devout swanBOT
#

https://100.<tailscale-ip>:18789/ won’t work in the default setup.

Two key points:

  1. By default the Gateway binds to loopback (127.0.0.1), so nothing is listening on the droplet’s 100.x Tailnet IP → your Mac can’t connect.
  2. Port 18789 is plain HTTP, not HTTPS (unless you put it behind Tailscale Serve / another TLS proxy).

You’ve got two good ways forward — pick one:


Option A (recommended): Tailscale Serve (gives you HTTPS)

On the droplet:

openclaw config set gateway.bind "loopback"
#
openclaw config set gateway.tailscale.mode "serve"
openclaw gateway restart

Then on your Mac, open (no port, HTTPS):

  • https://ubuntu-******/ (use the MagicDNS name you saw in tailscale status)

If it asks for auth, grab the token on the droplet:

openclaw config get gateway.auth.token

Docs: https://docs.openclaw.ai/gateway/tailscale

#

Option B: Bind directly to Tailnet IP (simpler, but HTTP)

On the droplet:

openclaw config set gateway.bind "tailnet"
openclaw gateway restart

Then on your Mac open HTTP (not https):

  • http://100.<ubuntu_ip>:18789/

Quick sanity checks (if it still doesn’t load)

#

On the droplet, paste:

openclaw gateway status
openclaw config get gateway.bind
openclaw config get gateway.tailscale.mode

Those 3 outputs will tell immediately which mode you’re in and what URL should work.

slim isle
#

openclaw@ubuntu-s-1vcpu-2gb-ams3-01:~$ openclaw config get gateway.tailscale

🦞 OpenClaw 2026.3.2 (85377a2)
Running on your hardware, reading your logs, judging nothing (mostly).

{
"mode": "serve",
"resetOnExit": false
}
openclaw@ubuntu-s-1vcpu-2gb-ams3-01:~$

but the link https://ubuntu...... not working.. tried https://ip, https://ip::18789, http://ip, http://ip::18789.. none of them are working

devout swanBOT
#

If gateway.tailscale.mode is "serve", then none of these should be used:

  • https://100.x.x.x:18789 (wrong: 18789 is the gateway’s plain HTTP port)
  • http://100.x.x.x:18789 (might work only if you’re not in Serve mode / if you bind to tailnet)
  • anything with :18789 in Serve mode

In Serve mode, the URL is HTTPS on 443 with NO port, like:

  • https://ubuntu-s-1vcpu-2gb-ams3-01/ (or sometimes a full …ts.net name)

1) First: check whether Tailscale Serve is actually configured on the droplet

On the droplet run:

tailscale serve status
#
  • If this shows an HTTPS handler / URL → open exactly that URL on your Mac.
  • If it says there’s no serve config (or errors about HTTPS/MagicDNS) → OpenClaw hasn’t successfully applied Serve yet.

2) Force apply by restarting the gateway

After setting gateway.tailscale.mode: "serve", do:

openclaw gateway restart

Then re-check:

tailscale serve status

3) If hostname doesn’t resolve on the Mac

#

On your Mac, run:

tailscale status

If it shows a longer name like something.tailnet-xyz.ts.net, try opening that full name in the browser.


Important: when you paste outputs here, redact any #token=... URLs or auth tokens (this is a public channel).

If you paste the output of tailscale serve status (usually safe), and tell me what the browser error is for https://ubuntu-.../ (DNS not found vs connection refused vs cert warning), I can tell you the exact next step.

slim isle
#

openclaw@ubuntu-s-1vcpu-2gb-ams3-01:~$ openclaw config get gateway

🦞 OpenClaw 2026.3.2 (85377a2) — Alexa, but with taste.

{
"port": 18789,
"mode": "local",
"bind": "loopback",
"auth": {
"mode": "token",
"token": "OPENCLAW_REDACTED"
},
"tailscale": {
"mode": "serve",
"resetOnExit": false
},
"nodes": {
"denyCommands": [
"camera.snap",
"camera.clip",
"screen.record",
"contacts.add",
"calendar.add",
"reminders.add",
"sms.send"
]
}
}
openclaw@ubuntu-s-1vcpu-2gb-ams3-01:~$ tailscale serve status
No serve config
openclaw@ubuntu-s-1vcpu-2gb-ams3-01:~$

devout swanBOT
#

That explains it: tailscale serve status → No serve config means Tailscale isn’t actually proxying anything yet, so https://ubuntu-…/ won’t exist.

Let’s wire Tailscale Serve up to the Gateway (which is on 127.0.0.1:18789 since you’re bind: "loopback").

1) Confirm the Gateway is reachable locally on the droplet

On the droplet:

curl -I http://127.0.0.1:18789/

You should get something back (often 200 or 401), but not “connection refused”.

2) Configure Tailscale Serve to reverse-proxy to the Gateway

On the droplet (Serve config usually needs sudo):

#
sudo tailscale serve --yes localhost:18789

Now check:

tailscale serve status

It should print a URL you can open from your Mac (usually https://ubuntu-s-.../ or an …ts.net name). Open exactly what tailscale serve status prints (no :18789).

3) Auth in the Control UI

Since your gateway auth mode is token, the UI will ask for it. Don’t paste it here, but on the droplet you can retrieve it with:

#
openclaw config get gateway.auth.token

Paste that into the Control UI “Gateway Token” field.


If sudo tailscale serve --yes localhost:18789 errors, paste the error text (safe), and we’ll fix the specific Tailscale setting it’s complaining about (often HTTPS/MagicDNS not enabled in the tailnet).

slim isle
#

openclaw@ubuntu-s*******:~$ sudo tailscale serve --yes localhost:18789
[sudo] password for openclaw:

Serve is not enabled on your tailnet.
To enable, visit:

     https://login.tailscale.com/f/serve?node=ngMK8TXczT11CNTRL

Success.
Available within your tailnet:

https://ubuntu-s-********.ts.net/
|-- proxy http://localhost:18789

Press Ctrl+C to exit.
^Copenclaw@ubuntu-s-1vcpu-2gb-ams3-01:~$
openclaw@ubuntu-s-1vcpu-2gb-ams3-01:~$
openclaw@ubuntu-s-1vcpu-2gb-ams3-01:~$ tailscale serve status
No serve config
openclaw@ubuntu-s-1vcpu-2gb-ams3-01:~$
openclaw@ubuntu-s-1vcpu-2gb-ams3-01:~$ openclaw gateway restart

🦞 OpenClaw 2026.3.2 (85377a2)
I'm not magic—I'm just extremely persistent with retries and coping strategies.

Restarted systemd service: openclaw-gateway.service
openclaw@ubuntu-s-1vcpu-2gb-ams3-01:~$ tailscale serve status
No serve config
openclaw@ubuntu-s-1vcpu-2gb-ams3-01:~$

devout swanBOT
#

Yep — what you’re seeing is exactly because you ran Serve in foreground mode.

When you did:

sudo tailscale serve --yes localhost:18789

it printed “Press Ctrl+C to exit.” When you hit Ctrl+C, it stopped (and effectively removed the active serve), so:

tailscale serve status

goes back to “No serve config”.

Make it persistent: run Serve in background

#

On the droplet run:

sudo tailscale serve --yes --bg localhost:18789

Then verify:

tailscale serve status

Now it should show the proxy config, and from your Mac you open:

  • https://ubuntu-s-********.ts.net/ (the exact URL it prints)

No :18789 in the browser URL.

#

If --bg is not recognized

Run:

tailscale serve --help | head -n 40

…and paste that output (safe). Tailscale changed the Serve CLI around v1.52, so I’ll match the exact flags to your version.

(And still: don’t paste your OpenClaw token into the channel.)

slim isle
#

i am able to access controlUI.. but now

message on ControlUI:
origin not allowed (open the Control UI from the gateway host or allow it in gateway.controlUi.allowedOrigins)

devout swanBOT
#

That error is the Gateway’s WebSocket Origin check kicking in (good security default). Since you’re opening the Control UI from a non-loopback origin (https://ubuntu-…ts.net), you must explicitly allow that origin.

Fix: allow your Tailscale Serve origin

On the droplet, add the exact origin you’re using in the browser address bar:

openclaw config set gateway.controlUi.allowedOrigins '["https://ubuntu-s-********.ts.net"]' --strict-json
openclaw gateway restart

Notes:

  • Put only the origin in that list: scheme://host (no path, no query, no trailing /).
  • Use the exact hostname you open in the browser (the one tailscale serve printed).

After restart, reload the Control UI page.

Docs (same setting): https://docs.openclaw.ai/web/control-ui and https://docs.openclaw.ai/gateway/configuration-reference

#

If you tell me the exact URL you’re opening (just the domain part is fine; no tokens), I can tell you the exact allowedOrigins value to paste.

slim isle
#

conrol UI now:

pairing required
This device needs pairing approval from the gateway host.
openclaw devices list
openclaw devices approve <requestId>
On mobile? Copy the full URL (including #token=...) from openclaw dashboard --no-open on your desktop.
Docs: Device pairing

devout swanBOT
#

Nice — you’re basically there. That message means the browser Control UI is a “new device” and the gateway requires you to approve the pairing once.

Approve the Control UI device (run on the droplet / gateway host)

On the droplet:

openclaw devices list
openclaw devices approve
  • devices approve (with no args) approves the most recent pending request.
  • If you prefer explicit approval, use the requestId from devices list:
openclaw devices approve <requestId>
#

Then reload the Control UI page.

Docs: https://docs.openclaw.ai/cli/devices

If you don’t see a pending request

Open/reload the Control UI again (so it re-attempts pairing), then re-run openclaw devices list.

Once this works, you should be fully connected (no more “pairing required”).

slim isle
#

it's working now...

why two devices being showed openclaw devices list with tokens: operator, roles:operator, and different scopes:

  1. operator.admin, operator.approvals, operator.pairing
  2. operator.admin, operator.read, operator.write, operator.approvals,operator.pairing
devout swanBOT
#

Totally normal, and good question — the short version is:

Why you see 2 “devices”

openclaw devices list is showing paired client identities. Each distinct client environment can become its own “device”, for example:

  • your Control UI browser (and even different browser profiles / private windows)
  • a CLI somewhere else (if you ever connect from another machine)
  • sometimes even the same browser but on a different origin (e.g. you previously opened the UI on a different hostname)

In your case, you went through a few different URLs/origins during setup (…ts.net, IP, etc.). Since the Control UI stores its device token in localStorage per-origin, changing the origin can trigger a new pairing → new device entry.

Why the scopes differ

Scopes are the permissions that particular device token has. Different clients (or different versions/flows) may request slightly different sets, e.g.:

  • operator.read / operator.write → normal “use the UI / run stuff / send messages”
  • operator.approvals → approve exec approvals
  • operator.pairing → manage pairing + rotate/revoke tokens
#
  • operator.admin → admin/control-plane privileges

So one entry is basically “operator token with pairing/approvals/admin”, and the other is “operator token with the usual read/write plus the rest”.

What to do about it

  • If you don’t like the extra one: remove the stale device (pick the one you aren’t using / older / not “last seen” recently):
openclaw devices list
openclaw devices remove <deviceId>
  • Also: don’t paste the token values in chat. If you ever did, rotate/revoke them.

If you paste the non-secret parts of openclaw devices list (deviceId + label + lastSeen, but redact tokens), I can tell you which one is safe to delete.

Glad it’s working now — if this thread solved it, please mark the solution message in Discord so Answer Overflow indexes it for the next person.

slim isle
#

generate a summary (builder summary) which i can copy paste in my notes, if i ever i have to setup tailscale next time on a new server... make sure it is command heavy and text lite

devout swanBOT
#
# Goal: Access OpenClaw Control UI on a remote Ubuntu server via Tailscale Serve (tailnet-only HTTPS)

########################
# SERVER (Ubuntu / DO)
########################

# sanity
openclaw --version
openclaw gateway status

# tailscale up (do this however you prefer; verify it’s online)
tailscale status
tailscale ip -4

# OpenClaw: keep gateway on loopback + declare tailscale serve intent
#
openclaw config set gateway.bind "loopback"
openclaw config set gateway.tailscale.mode "serve"
openclaw gateway restart

# confirm gateway is reachable locally
curl -I http://127.0.0.1:18789/

# Tailscale Serve: reverse-proxy HTTPS -> http://localhost:18789 (PERSISTENT)
sudo tailscale serve --yes --bg localhost:18789
tailscale serve status         # copy the printed https://<host>.ts.net/ URL

# allow the Control UI browser origin (IMPORTANT: scheme+host only)
openclaw config set gateway.controlUi.allowedOrigins '["https://<host>.ts.net"]' --strict-json
openclaw gateway restart
#
# get auth token (SECRET; don’t paste in public)
openclaw config get gateway.auth.token
openclaw dashboard --no-open   # prints URL (often tokenized); treat as SECRET

# if Control UI says “pairing required”:
openclaw devices list
openclaw devices approve       # approves latest pending request
# or:
openclaw devices approve <requestId>

########################
# LAPTOP (Mac)
########################

# install + login Tailscale app
#
# verify both devices are on the same tailnet:
tailscale status

# open in browser:
#   https://<host>.ts.net/
# paste Gateway Token in Control UI when prompted

########################
# OPTIONAL CLEANUP / DEBUG
########################

tailscale serve status
sudo tailscale serve reset

openclaw config get gateway
#
openclaw devices list          # shows paired devices (tokens are secrets)
openclaw devices remove <deviceId>
slim isle
#

delete the device, and got success message.. but the same device appeared again

devout swanBOT
#

That’s expected if the device is still trying to connect.

openclaw devices remove <deviceId> = “revoke/unpair this device token”, not “block it forever”.

So if you remove the device that your Control UI tab is using:

  1. the UI reconnects
  2. the gateway says “not paired anymore”
  3. the UI immediately starts a new pairing flow
  4. the device shows up again (often as a pending request)

What you probably want (remove duplicates, keep the active one)

  1. On your laptop: close extra Control UI tabs / other browsers (and don’t open via multiple different URLs like IP vs *.ts.net)
  2. On the server:
#
openclaw devices list
  1. Remove only the stale ones (older / not “last seen recently”):
openclaw devices remove <staleDeviceId>

If you want a totally clean slate (more aggressive)

  1. Close the Control UI tab
  2. On server:
openclaw devices clear --yes
#
  1. Re-open Control UI → approve the new pairing once.

(Only do devices clear if you’re sure you don’t need any existing paired devices.)

slim isle
#

it is getting auto-approved

devout swanBOT
#

That’s expected in your setup, and it’s usually one of these two things:

A) The Control UI is still open, so it immediately reconnects

If the browser tab is open (or auto-reloading), it will reconnect and the device will show up again.

Fix: close the Control UI tab(s) first, then delete/revoke.

B) You’re accessing via Tailscale Serve → localhost, so it can look “local”

Tailscale Serve is proxying to http://127.0.0.1:18789. OpenClaw treats loopback clients as “local”, and local connections can be auto-approved (docs even call this out).

So after you remove it, it reconnects and gets “auto-approved” again because it’s effectively arriving from loopback.


If your goal is “make it go away and stay gone”

  1. Close the Control UI tab(s)
  2. Revoke the operator token for that device (stronger than just removing the row):