#How to make tailscale work inside a docker container?
1 messages Β· Page 1 of 1 (latest)
No β you donβt need Tailscale inside the OpenClaw container in the common setup.
Typical / recommended setup (simplest)
- Install Tailscale on the Docker host (VM/RPi/NAS/etc)
- Run OpenClaw in Docker as usual
- Publish the OpenClaw ports on the host (or use host networking), then access it via the hostβs Tailscale IP / MagicDNS name
Why: avoids NET_ADMIN, /dev/net/tun, iptables weirdness, and is much easier to debug.
When you would run Tailscale in/with containers
Only if you want the container to be its own Tailnet device (separate Tailscale IP, ACL identity tags, etc.). Then youβd run Tailscale as:
- a sidecar container sharing the network namespace with OpenClaw (
--network=container:<openclaw>), or - Tailscale directly in the same container (usually needs
/dev/net/tun+--cap-add=NET_ADMIN/ userspace mode)
If you tell me what youβre trying to achieve (access the dashboard remotely? expose webhooks? multiple containers each with their own Tailnet identity?), I can point you to the cleanest pattern.
My openclaw is installed inside a hostinger container
If βHostinger containerβ is the only OS layer you can SSH into / control, then treat it like the host: install Tailscale there (or run it as a sidecar container).
But thereβs a catch: a lot of managed container products donβt allow /dev/net/tun / NET_ADMIN, so βnormalβ Tailscale wonβt start.
Quick decision tree
-
You have a VPS and youβre just running Docker on it
β Install Tailscale on the VPS itself (recommended), and just expose OpenClawβs port(s) on localhost/host. -
You only have access to the container (no host access)
β Youβll likely need Tailscale inside that container, and either:
- run the container privileged enough for Tailscale (TUN + NET_ADMIN), or
- use userspace networking (works without TUN):
tailscaled --tun=userspace-networking ...
then usetailscale serveto publish OpenClaw over Tailnet.
If you paste what Hostinger product this is (VPS? βDocker container hostingβ? LXC?), and how youβre starting the OpenClaw container (docker run / compose), I can tell you the exact setup thatβll work there.
It's my VPS so I can install things on it
Perfect β if itβs a VPS you control, the clean setup is:
- Install + run Tailscale on the VPS (host OS)
- Keep OpenClaw in Docker as-is (no Tailscale inside the container)
- Access OpenClaw via the VPSβs Tailscale IP / MagicDNS name
Minimal approach (works if you publish the port)
- Make sure your OpenClaw container publishes its UI/API port to the host (Docker
ports:or-p ...). - Then you can hit it at:
http://<vps-tailscale-ip>:<published-port>
(and you donβt need to open that port to the public internetβjust allow it on the VPS firewall for thetailscale0interface, if youβre firewalling tightly).
βDonβt expose ports at allβ approach (my favorite)
Bind OpenClaw only to localhost on the VPS, then let Tailscale proxy it:
- Publish container port to 127.0.0.1 on the VPS (example):
- Docker run:
-p 127.0.0.1:PORT:PORT - Compose:
ports: ["127.0.0.1:PORT:PORT"]
- Then on the VPS:
sudo tailscale serve http://127.0.0.1:PORT
Now itβs reachable only over Tailnet (nice and safe).
If you tell me what port your OpenClaw dashboard is on (or paste your docker run / compose ports: block), Iβll give you the exact command lines.
okay so in steps how to install
Yep β if you control the VPS, install Tailscale on the VPS host OS (not inside the OpenClaw container). Hereβs a straightforward step-by-step.
0) SSH in + check distro
ssh root@YOUR_VPS_IP
cat /etc/os-release
If itβs Ubuntu/Debian, the steps below work as-is. (If itβs something else, paste /etc/os-release and Iβll adjust.)
1) Install Tailscale
Fast + usually fine on servers:
sudo apt-get update
sudo apt-get install -y curl
curl -fsSL https://tailscale.com/install.sh | sh
2) Bring Tailscale up (login)
Interactive login (it will print a URL you open in your browser):
sudo tailscale up
Verify:
tailscale status
tailscale ip -4
That tailscale ip -4 is the IP youβll use from your laptop/phone on the same Tailnet.
3) Expose OpenClaw safely over Tailnet
Option A (simple): access published port via Tailscale IP
If your OpenClaw container publishes a port to the VPS, you can hit:
http://<tailscale-ip>:<port>
Check what Docker is publishing:
docker ps --format "table {{.Names}}\t{{.Ports}}"
Option B (recommended): donβt expose ports publicly, use tailscale serve
Bind the OpenClaw port to localhost on the VPS (so itβs not on the public internet), then proxy it over Tailnet.
- Make sure Docker publishes to
127.0.0.1(example format):
docker run:-p 127.0.0.1:PORT:PORTdocker-compose.yml:ports: ["127.0.0.1:PORT:PORT"]
- Then:
sudo tailscale serve http://127.0.0.1:PORT
Now you can open it from another Tailnet device via:
http://<vps-hostname>:80 (or whatever tailscale serve status shows)
If you paste the output of:
docker ps --format "table {{.Names}}\t{{.Ports}}"and/or- what
openclaw dashboardprints on the VPS
β¦Iβll tell you exactly what PORT to use and the cleanest Option A vs B for your setup.
I did up to step 2 how do I go through step 3
Step 3 = βmake OpenClaw reachable via the VPSβs Tailscale networkβ. There are two ways; pick one.
First: find what port OpenClaw is on
On the VPS run:
docker ps --format "table {{.Names}}\t{{.Ports}}"
Copy/paste the output here if youβre unsure what to pick.
Youβre looking for something like:
0.0.0.0:3210->3210/tcpor127.0.0.1:3210->3210/tcp
The left side (3210 above) is the host/VPS port you connect to.
Option A (simplest): connect directly to the published port
- Get the VPS Tailscale IP:
tailscale ip -4
- From your laptop/phone (also on Tailscale), open:
http://<TAILSCALE_IP>:<HOST_PORT>
If your docker ps shows 0.0.0.0:<HOST_PORT>->... this should βjust workβ.
Option B (recommended): keep it off the public internet + use tailscale serve
1) Ensure Docker publishes OpenClaw only on localhost
Make it bind to 127.0.0.1 (example):
- docker run:
-p 127.0.0.1:3210:3210 - docker compose:
ports:
- "127.0.0.1:3210:3210"
Restart the container after changing ports.
2) Turn on Tailscale proxying
sudo tailscale serve http://127.0.0.1:3210
Check what URL itβs serving on:
tailscale serve status
To undo:
sudo tailscale serve reset
If you paste the docker ps ... output, Iβll tell you exactly which <HOST_PORT> to use and whether Option A will work immediately or if you need to change your port bindings.
root@srv1100186:~# docker ps --format "table {{.Names}}\t{{.Ports}}"
NAMES PORTS
openclaw-zs4q-openclaw-1 0.0.0.0:43710->43710/tcp, [::]:43710->43710/tcp
root@srv1100186:~#
Nice β OpenClaw is already published on the VPS on port 43710, so Step 3 is basically βconnect to it over Tailscaleβ.
Option A (do this now β simplest)
- Get the VPS Tailscale IP:
tailscale ip -4
- On your laptop/phone (also logged into the same Tailnet), open:
http://<TAILSCALE_IP>:43710
That should hit the OpenClaw UI directly.
If it doesnβt load
On the VPS, check if something is firewalling that port:
- If you use ufw, allow it on the Tailscale interface:
sudo ufw allow in on tailscale0 to any port 43710 proto tcp
Option B (more secure): donβt expose 43710 publicly
Right now itβs bound to 0.0.0.0 (public internet). Better is localhost-only + tailscale serve.
- Change your Docker/compose port mapping to bind to localhost:
- Compose:
ports:
- "127.0.0.1:43710:43710"
Then restart:
docker compose up -d
- Serve it over Tailnet:
sudo tailscale serve http://127.0.0.1:43710
tailscale serve status
If you tell me whether youβre using docker compose (and can paste the ports: section), I can give you the exact edit so it stops listening on the public internet.
ports:
- "127.0.0.1:43710:43710"
run this on the vps?
That ports: line is not a command β itβs a snippet that goes into your docker-compose.yml on the VPS (because Docker is running there).
What to do (on the VPS)
- Open your
docker-compose.yml(wherever your OpenClaw compose file lives) and set:
services:
openclaw:
ports:
- "127.0.0.1:43710:43710"
- Apply the change:
docker compose up -d
- Verify itβs no longer public:
docker ps --format "table {{.Names}}\t{{.Ports}}"
You should see 127.0.0.1:43710->43710/tcp (not 0.0.0.0:...).
Then (optional, recommended): expose via Tailscale Serve
sudo tailscale serve http://127.0.0.1:43710
tailscale serve status
If you paste your compose fileβs service name (it might not be exactly openclaw), I can show the exact indentation/placement so it validates.
root@srv1100186:~# services:
openclaw:
ports:
- "127.0.0.1:43710:43710"
services:: command not found
openclaw:: command not found
ports:: command not found
-: command not found
Yep β you pasted YAML config into the shell, so bash tried to run services: as a command. That ports: block must go into your docker-compose.yml file.
1) Find the compose file Docker is using
Run this on the VPS:
docker inspect openclaw-zs4q-openclaw-1 --format '{{ index .Config.Labels "com.docker.compose.project.config_files" }}'
docker inspect openclaw-zs4q-openclaw-1 --format '{{ index .Config.Labels "com.docker.compose.project.working_dir" }}'
It should print the compose file path and working directory.
2) Edit the compose file (on the VPS)
cd into the working dir from the second command, then edit the compose file (path from the first command). For example:
cd /path/from/working_dir
nano /path/from/config_files
Make sure the indentation is exactly like this:
services:
openclaw:
ports:
- "127.0.0.1:43710:43710"
3) Apply the change
From that same directory:
docker compose up -d
docker ps --format "table {{.Names}}\t{{.Ports}}"
You should now see 127.0.0.1:43710->43710/tcp (not 0.0.0.0:...).
4) (Recommended) Expose it over Tailscale
sudo tailscale serve http://127.0.0.1:43710
tailscale serve status
If you paste the output of the two docker inspect ... commands, Iβll tell you the exact nano path + the exact docker compose -f ... up -d command for your case.
root@srv1100186:~# docker inspect openclaw-zs4q-openclaw-1 --format '{{ index .Config.Labels "com.docker.compose.project.config_files" }}'
docker inspect openclaw-zs4q-openclaw-1 --format '{{ index .Config.Labels "com.docker.compose.project.working_dir" }}'
/docker/openclaw-zs4q/docker-compose.yml
/docker/openclaw-zs4q
Great β youβve got the exact paths.
Now do this on the VPS:
1) Edit the compose file
cd /docker/openclaw-zs4q
nano docker-compose.yml
Find the ports: for the OpenClaw service and change it to:
ports:
- "127.0.0.1:43710:43710"
(Indentation matters: ports: should be under the OpenClaw service.)
Save + exit in nano: Ctrl+O, Enter, then Ctrl+X.
2) Recreate the container with the new port binding
docker compose up -d
3) Verify itβs no longer public
docker ps --format "table {{.Names}}\t{{.Ports}}"
You want to see 127.0.0.1:43710->43710/tcp (NOT 0.0.0.0:43710->...).
4) (Optional but recommended) Expose it via Tailscale only
sudo tailscale serve http://127.0.0.1:43710
tailscale serve status
If you paste the services: section from your docker-compose.yml (just that part), I can confirm the exact indentation/service name so it wonβt fail YAML parsing.
services:
openclaw:
image: ghcr.io/hostinger/hvps-openclaw:latest
init: true
ports:
- "${PORT}:${PORT}"
env_file:
- .env
restart: unless-stopped
volumes:
- ./data:/data
- ./data/linuxbrew:/home/linuxbrew
Yep β in your compose file you need to change the ports: mapping.
Right now you have:
ports:
- "${PORT}:${PORT}"
Change it to bind only to localhost:
ports:
- "127.0.0.1:${PORT}:${PORT}"
Then on the VPS (in /docker/openclaw-zs4q):
docker compose up -d
docker ps --format "table {{.Names}}\t{{.Ports}}"
You should now see 127.0.0.1:43710->43710/tcp instead of 0.0.0.0:43710->....
Next: expose it over Tailscale
sudo tailscale serve http://127.0.0.1:43710
tailscale serve status
From your other device on Tailscale, open whatever hostname/URL tailscale serve status shows.
If ${PORT} isnβt always 43710 (i.e. it changes), tell me whatβs in your .env for PORT= and Iβll suggest the most stable way to serve it.
root@srv1100186:~# cd /docker/openclaw-zs4q
nano docker-compose.yml
root@srv1100186:/docker/openclaw-zs4q# docker compose up -d
docker ps --format "table {{.Names}}\t{{.Ports}}"
[+] up 1/1
β Container openclaw-zs4q-openclaw-1 Recreated 4.2s
NAMES PORTS
openclaw-zs4q-openclaw-1 127.0.0.1:43710->43710/tcp
root@srv1100186:/docker/openclaw-zs4q#
Perfect β thatβs exactly what we wanted: itβs now only listening on localhost (127.0.0.1:43710), so itβs no longer exposed to the public internet.
Now expose it over Tailscale:
1) Serve it via Tailscale
Run on the VPS:
sudo tailscale serve http://127.0.0.1:43710
2) See the URL / status
tailscale serve status
Itβll show what hostname + port itβs available on (usually your VPSβs MagicDNS name on port 80/443 depending on serve config).
3) Open it from your other device (on Tailnet)
Use the hostname shown by tailscale serve status, e.g.
http://<your-vps-name>(if it maps to port 80), or- whatever exact URL it prints.
If you ever want to undo it
sudo tailscale serve reset
If you paste the output of tailscale serve status, Iβll tell you the exact URL to open.
NAMES PORTS
openclaw-zs4q-openclaw-1 127.0.0.1:43710->43710/tcp
root@srv1100186:/docker/openclaw-zs4q# sudo tailscale serve http://127.0.0.1:43710
Available within your tailnet:
https://srv1100186.tail777ba9.ts.net/
|-- proxy http://127.0.0.1:43710
Press Ctrl+C to exit.
Awesome β itβs working.
From any device on your Tailnet, open:
https://srv1100186.tail777ba9.ts.net/
Thatβs now proxying to your OpenClaw at 127.0.0.1:43710.
One important detail: the βPress Ctrl+C to exitβ means this tailscale serve command is currently running in the foreground. It usually persists after you Ctrl+C (because it updates Tailscaleβs serve config), but to confirm itβs set:
tailscale serve status
If you ever want to remove the proxy:
sudo tailscale serve reset
Press Ctrl+C to exit.
^Croot@srv1100186:/docker/openclaw-zs4q# tailscale serve status
No serve config
root@srv1100186:/docker/openclaw-zs4q#
Yep β that behavior is expected: without --bg, tailscale serve runs a foreground session, and Ctrl+C removes it, so you end up with βNo serve configβ.
Run it again, but in background/persistent mode:
sudo tailscale serve --bg http://127.0.0.1:43710
Then confirm it stuck:
tailscale serve status
Now you can Ctrl+C / logout and it should still be served at the https://<your-vps>.ts.net/ URL it prints.
Press Ctrl+C to exit.
^Croot@srv1100186:/docker/openclaw-zs4q# tailscale serve status
No serve config
root@srv1100186:/docker/openclaw-zs4q# sudo tailscale serve --bg http://127.0.0.1:43710
Available within your tailnet:
https://srv1100186.tail777ba9.ts.net/
|-- proxy http://127.0.0.1:43710
Serve started and running in the background.
To disable the proxy, run: tailscale serve --https=443 off
root@srv1100186:/docker/openclaw-zs4q#
Perfect β thatβs the βstickyβ setup.
Now it should survive logout/reboots. Confirm with:
tailscale serve status
And the URL to use from your other Tailscale device is:
https://srv1100186.tail777ba9.ts.net/
If you ever want to turn it off later:
sudo tailscale serve --https=443 off
Glad that worked β if this fixed it, please mark the solution (right-click the message β Apps β β Mark Solution) so Answer Overflow indexes it.
cool, should I change anything here to remove sensitive information or change anything in my config
Youβre mostly good as-is β you already made the big security improvement by changing Docker from 0.0.0.0:43710 (public internet) to 127.0.0.1:43710 (localhost only). That means random people on the internet canβt hit OpenClaw directly anymore.
A few practical βsanity/securityβ checks:
-
Donβt paste secrets
Avoid sharing your.env/ OpenClaw config if it contains any of: API keys (OpenAI/Anthropic/etc), gateway tokens, webhook secrets.
Also: the URL thatopenclaw dashboardprints often contains a#token=...β treat that as a secret. -
Make sure you didnβt accidentally enable public access (Funnel)
Your*.ts.netURL is only reachable by devices in your Tailnet unless Funnel is enabled.
You can double-check whatβs exposed with:
tailscale serve status
(If you see anything about βfunnelβ being on, turn it off in Tailscale or tell me what it prints and Iβll guide.)
- Close any leftover public firewall rules (optional but nice)
If you previously opened 43710 in UFW/cloud firewall, you can remove that rule since it no longer needs to be public. (Not strictly required because itβs bound to localhost now, but it reduces confusion.)
Config-wise for OpenClaw: nothing special needed for Tailscale; this is all networking outside the container.
root@srv1100186:/docker/openclaw-zs4q# tailscale serve status
https://srv1100186.tail777ba9.ts.net (tailnet only)
|-- / proxy http://127.0.0.1:43710
That status is ideal:
tailnet only= not public, no Funnel- proxying to
127.0.0.1:43710= OpenClaw is not exposed on the public interface
So: no config changes required for security beyond what you already did.
Only βsensitiveβ things to be careful with going forward:
- Donβt share your
.env/ OpenClaw config values that include API keys/tokens. - Donβt post the
openclaw dashboardURL if it contains#token=...(treat that like a password).
If you want to harden a bit more: ensure your VPS firewall only allows SSH from your IP (or from Tailnet), but thatβs optional and separate from OpenClaw/Tailscale.
anything else I can do to improve security?
You already did the biggest win: OpenClaw is no longer bound to the public internet (localhost-only + tailscale serve tailnet-only). Nice.
Highest-ROI security improvements for a VPS next:
1) Run the built-in OpenClaw security audit (read-only)
Since OpenClaw is in Docker, run it inside the container:
docker exec -it openclaw-zs4q-openclaw-1 openclaw security audit --deep
docker exec -it openclaw-zs4q-openclaw-1 openclaw update status
If the audit suggests fixes, itβll usually offer --fix (that only adjusts OpenClaw defaults/permissions, not your firewall/SSH).
2) SSH hardening (big one, but donβt lock yourself out)
- Use SSH keys only (disable password auth)
- Disable root login over SSH
- Optionally restrict SSH to Tailscale only (or at least firewall it to your IP)
3) Firewall: βdeny inbound by defaultβ
Turn on a host firewall (ufw/nftables) with a minimal allowlist:
- allow SSH (ideally only from Tailnet or your IP)
- you do not need to allow OpenClaw ports publicly anymore
4) Update + backups
- Enable automatic security updates (Ubuntu/Debian:
unattended-upgrades) - Make sure you have provider snapshots/backups enabled (this saves you when you break SSH/firewall)
To keep this safe, pick what you want to do next (reply with a number):
- Audit OpenClaw only (paste output, Iβll interpret)
- Harden SSH (Iβll guide step-by-step, access-preserving)
- Set up firewall (ufw) (deny inbound, keep Tailnet access)
- All of the above (in the safest order)
Also: how are you currently SSHβing in β via public IP or via Tailscale? That determines how aggressive we can be with SSH/firewall without risking lockout.
CRITICAL
gateway.control_ui.insecure_auth Control UI allows insecure HTTP auth
gateway.controlUi.allowInsecureAuth=true allows token-only auth over HTTP and skips device identity.
Fix: Disable it or switch to HTTPS (Tailscale Serve) or localhost.
WARN
fs.auth_profiles.perms_readable auth-profiles.json is readable by others
/data/.openclaw/agents/main/agent/auth-profiles.json mode=644; auth-profiles.json contains API keys and OAuth tokens.
Fix: chmod 600 /data/.openclaw/agents/main/agent/auth-profiles.json
INFO
summary.attack_surface Attack surface summary
groups: open=0, allowlist=1
tools.elevated: enabled
hooks: disabled
browser control: enabled
OpenClaw update status
ββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Item β Value β
ββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Install β pnpm β
β Channel β stable (default) β
β Update β available Β· pnpm Β· npm update 2026.2.14
Good audit output β there are two concrete fixes you should do, plus a couple βnice to havesβ.
Do these 2 now
1) Fix readable creds file (WARN)
That file can contain API keys / OAuth tokens and is currently world-readable (644). Run on the VPS:
docker exec -u root openclaw-zs4q-openclaw-1 chmod 600 /data/.openclaw/agents/main/agent/auth-profiles.json
docker exec -u root openclaw-zs4q-openclaw-1 ls -l /data/.openclaw/agents/main/agent/auth-profiles.json
2) Disable insecure Control UI auth (CRITICAL)
Since youβre already using HTTPS via Tailscale Serve, you shouldnβt need gateway.controlUi.allowInsecureAuth=true.
Find where itβs set (run on VPS):
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'grep -RIn "allowInsecureAuth" /data 2>/dev/null || true'
Then edit the config it points to and set it to false (or remove it). After editing, restart:
docker compose up -d
(Reference: https://docs.openclaw.ai/gateway/security and https://docs.openclaw.ai/web/control-ui)
Verify
Re-run:
docker exec -it openclaw-zs4q-openclaw-1 openclaw security audit --deep
Optional improvements (pick what you want)
- Update (since
2026.2.14is available) via your container image workflow:docker compose pull && docker compose up -d - SSH hardening (keys-only, disable root login; optionally allow SSH only via Tailnet)
- Turn off tools.elevated if you donβt need the agent running elevated commands (least privilege)
Which do you want next: 1 (fixes only), 2 (also update), or 3 (full VPS hardening: SSH + firewall + updates)?
π¦ OpenClaw 2026.2.12 (f9e444d) β I'm the assistant your terminal demanded, not the one your sleep schedule requested.
OpenClaw security audit
Summary: 1 critical Β· 0 warn Β· 1 info
Run deeper: openclaw security audit --deep
CRITICAL
gateway.control_ui.insecure_auth Control UI allows insecure HTTP auth
gateway.controlUi.allowInsecureAuth=true allows token-only auth over HTTP and skips device identity.
Fix: Disable it or switch to HTTPS (Tailscale Serve) or localhost.
INFO
summary.attack_surface Attack surface summary
groups: open=0, allowlist=1
tools.elevated: enabled
hooks: disabled
browser control: enabled
Youβve already fixed the file-perms warning β nice. The remaining CRITICAL is because gateway.controlUi.allowInsecureAuth=true is still enabled.
Since youβre now using HTTPS via Tailscale Serve, you can (and should) turn that off:
1) Disable insecure auth (run on the VPS)
docker exec -it openclaw-zs4q-openclaw-1 openclaw config set gateway.controlUi.allowInsecureAuth false --json
docker exec -it openclaw-zs4q-openclaw-1 openclaw config get gateway.controlUi.allowInsecureAuth
2) Restart OpenClaw (container)
docker restart openclaw-zs4q-openclaw-1
3) Re-run the audit
docker exec -it openclaw-zs4q-openclaw-1 openclaw security audit --deep
Note: after disabling it, the Control UI may stop working over plain HTTP (expected). Use your HTTPS Tailscale URL: https://srv1100186.tail777ba9.ts.net/.
If this clears the CRITICAL, please mark the solution (right-click β Apps β β Mark Solution) so itβs indexed.
root@srv1100186:/docker/openclaw-zs4q#
run in here>
Yes β run those commands in your VPS terminal (where you are now). The folder (/docker/openclaw-zs4q) doesnβt matter; docker exec ... targets the container directly.
Run:
docker exec -it openclaw-zs4q-openclaw-1 openclaw config set gateway.controlUi.allowInsecureAuth false --json
docker exec -it openclaw-zs4q-openclaw-1 openclaw config get gateway.controlUi.allowInsecureAuth
docker restart openclaw-zs4q-openclaw-1
docker exec -it openclaw-zs4q-openclaw-1 openclaw security audit --deep
After that, keep using the HTTPS Tailscale URL (https://srv1100186.tail777ba9.ts.net/). Plain http://... may stop working (thatβs the point of disabling insecure auth).
docker restart openclaw-zs4q-openclaw-1
docker exec -it openclaw-zs4q-openclaw-1 openclaw security audit --deep
Updated gateway.controlUi.allowInsecureAuth. Restart the gateway to apply.
π¦ OpenClaw 2026.2.12 (f9e444d) β The only bot that stays out of your training set.
false
openclaw-zs4q-openclaw-1
root@srv1100186:/docker/openclaw-zs4q#
Looks like you successfully set it β the false you see is the value of gateway.controlUi.allowInsecureAuth.
Whatβs missing is the post-restart audit output (your paste shows the config change + the restart, but not the new audit results).
Run this now on the VPS:
docker exec -it openclaw-zs4q-openclaw-1 openclaw security audit --deep
If the CRITICAL is gone, youβre done.
If it still complains about insecure auth:
- Make sure youβre opening the UI via HTTPS:
https://srv1100186.tail777ba9.ts.net/(nothttp://...) - Paste the audit output and also:
docker exec -it openclaw-zs4q-openclaw-1 openclaw config get gateway.controlUi
And small heads-up: now that insecure auth is disabled, the Control UI may ask to re-pair / use device identity (expected).
disconnected (1005): no reason
shows this in the UI
Thatβs expected right after you turned allowInsecureAuth off: the Control UI now requires device identity + one-time device pairing approval. On 2026.2.12 it can show up as a generic disconnected (1005).
Fix: approve the browser device
-
Keep the UI open at your HTTPS Tailnet URL:
https://srv1100186.tail777ba9.ts.net/ -
On the VPS, run:
docker exec -it openclaw-zs4q-openclaw-1 openclaw devices list
- You should see a pending pairing request. Approve it:
docker exec -it openclaw-zs4q-openclaw-1 openclaw devices approve <requestId>
- Refresh the UI page.
Notes:
- Donβt paste any tokens here. The
<requestId>is fine to share if you need help. - If the UI asks for a token, use your gateway token in the UI settings (but donβt post it publicly).
Id between the < > ?
Yep β the <requestId> is the pairing request ID that shows up in the output of:
docker exec -it openclaw-zs4q-openclaw-1 openclaw devices list
Itβll look something like:
requestId: abc123...(or a UUID-like string)
Then you run:
docker exec -it openclaw-zs4q-openclaw-1 openclaw devices approve abc123...
If you paste the output of openclaw devices list (it shouldnβt contain secrets), I can tell you exactly which value to use.
π¦ OpenClaw 2026.2.12 (f9e444d) β End-to-end encrypted, drama-to-drama excluded.
β
β
[openclaw] Failed to start CLI: Error: unknown requestId
at GatewayClient.handleMessage (file:///usr/local/lib/node_modules/openclaw/dist/client-CAw2wMY5.js:1641:25)
at WebSocket.<anonymous> (file:///usr/local/lib/node_modules/openclaw/dist/client-CAw2wMY5.js:1498:40)
at WebSocket.emit (node:events:519:28)
at Receiver.receiverOnMessage (/usr/local/lib/node_modules/openclaw/node_modules/ws/lib/websocket.js:1225:20)
at Receiver.emit (node:events:519:28)
at Receiver.dataMessage (/usr/local/lib/node_modules/openclaw/node_modules/ws/lib/receiver.js:596:14)
at Receiver.getData (/usr/local/lib/node_modules/openclaw/node_modules/ws/lib/receiver.js:496:10)
at Receiver.startLoop (/usr/local/lib/node_modules/openclaw/node_modules/ws/lib/receiver.js:167:16)
at Receiver._write (/usr/local/lib/node_modules/openclaw/node_modules/ws/lib/receiver.js:94:10)
at writeOrBuffer (node:internal/streams/writable:572:12)
root@srv1100186:/docker/openclaw-zs4q#
That error just means you tried to approve an ID that doesnβt exist (most commonly because you literally used the placeholder, or the request expired).
Do this:
-
Make sure the Control UI tab is open at HTTPS (
https://srv1100186.tail777ba9.ts.net/) and you see it trying to connect / showing disconnected. -
In the VPS terminal run:
docker exec -it openclaw-zs4q-openclaw-1 openclaw devices list
-
Look for a Pending pairing request and copy the exact
requestIdit prints (no< >). -
Approve it:
docker exec -it openclaw-zs4q-openclaw-1 openclaw devices approve REQUEST_ID_HERE
- Refresh the UI.
If devices list shows no pending requests, refresh the UI page once (to generate a new request) and run devices list again. Paste the devices list output here if you want me to point at the exact ID.
Fab works now
Nice β that was the device pairing step kicking in after disabling insecure auth.
If this thread solved it, please mark the solution (rightβclick the solution message β Apps β β Mark Solution) so Answer Overflow indexes it.
If you want a final safety check, re-run:
docker exec -it openclaw-zs4q-openclaw-1 openclaw security audit --deep
You should see 0 critical now.
Now, is it possible to let openclaw code a website on my vps with a accesible link?
Yes β but OpenClaw wonβt magically βhostβ a website by itself. The usual pattern is:
- Give OpenClaw a writable folder on the VPS (via a Docker volume mount) so it can create/edit site files.
- Allow OpenClaw to run commands (build/start) via the
exectool (ideally with an allowlist / non-root). - Expose the site with a link (either Tailnet-only via Tailscale Serve, or public via a domain / reverse proxy / Funnel).
Minimal setup (Tailnet-only link, easiest + safest)
A) Add a projects volume to your OpenClaw docker-compose.yml:
services:
openclaw:
volumes:
- ./data:/data
- ./projects:/projects
Then:
mkdir -p /docker/openclaw-zs4q/projects
docker compose up -d
B) Ask OpenClaw to generate the site into /projects/my-site (from the Control UI chat), e.g. βCreate a simple static site in /projects/my-siteβ¦β.
C) Start the site on localhost (example: 127.0.0.1:3000), then publish a Tailnet link:
sudo tailscale serve --bg http://127.0.0.1:3000
If you want it publicly accessible (anyone on the internet)
Youβd typically use a domain + Caddy/Nginx (ports 80/443), or Tailscale Funnel (more risk). Iβd only do public exposure if you explicitly want that.
Two quick questions so I can recommend the right approach:
- Should the website be Tailnet-only or public internet?
- What kind of site: static HTML, Next.js/React, or something else?
Can we start with tailnet for now give me the steps
Yes. Hereβs a clean βtailnet-only websiteβ setup that wonβt expose anything to the public internet.
Goal
- Website files live on the VPS in
./projects/site1 - Nginx serves them on localhost only (
127.0.0.1:3000) - Tailscale provides an HTTPS tailnet-only link (weβll use port 8443 so it doesnβt collide with your existing OpenClaw Serve on 443)
1) Create a website folder (on the VPS)
cd /docker/openclaw-zs4q
mkdir -p projects/site1
(Optional quick test file)
cat > projects/site1/index.html <<'EOF'
<!doctype html>
<html>
<head><meta charset="utf-8"><title>Hello</title></head>
<body><h1>Hello from my VPS (Tailnet only)</h1></body>
</html>
EOF
2) Update docker-compose.yml
Edit:
nano /docker/openclaw-zs4q/docker-compose.yml
A) Mount ./projects into OpenClaw (so OpenClaw can edit it)
Under your openclaw: service, add:
volumes:
- ./data:/data
- ./data/linuxbrew:/home/linuxbrew
- ./projects:/projects
B) Add a new service to serve the website
Add this alongside openclaw::
site1:
image: nginx:alpine
restart: unless-stopped
volumes:
- ./projects/site1:/usr/share/nginx/html:ro
ports:
- "127.0.0.1:3000:80"
Bring it up:
docker compose up -d
Verify it serves locally:
curl -I http://127.0.0.1:3000
3) Expose it on Tailnet via Tailscale Serve (HTTPS)
Because OpenClaw already uses https://srv1100186.../ on 443, weβll use 8443 for the website:
sudo tailscale serve --bg --https=8443 http://127.0.0.1:3000
tailscale serve status
Your site will be reachable (tailnet-only) at:
https://srv1100186.tail777ba9.ts.net:8443/
4) Let OpenClaw βcode the websiteβ
In the OpenClaw UI, tell it to edit files under:
/projects/site1/ (e.g. /projects/site1/index.html, add /projects/site1/style.css, etc.)
If you tell me whether you want a static site (HTML/CSS) or something like React/Next.js, Iβll give you the best βfirst promptβ for OpenClaw plus the best way to serve it.
general sites folder?
Yep β best practice is to make one βparentβ folder and put each site in its own subfolder.
Recommended layout (on the VPS)
In your OpenClaw compose directory (/docker/openclaw-zs4q):
./projects/
sites/
site1/
index.html
site2/
index.html
Create it:
cd /docker/openclaw-zs4q
mkdir -p projects/sites/site1
Mount it once into OpenClaw
In docker-compose.yml (OpenClaw service), keep a single mount:
volumes:
- ./projects:/projects
Then OpenClaw can write to:
/projects/sites/site1/.../projects/sites/site2/...
Serving: one Nginx container per site (simplest)
Example for site1:
site1:
image: nginx:alpine
restart: unless-stopped
volumes:
- ./projects/sites/site1:/usr/share/nginx/html:ro
ports:
- "127.0.0.1:3000:80"
For site2, copy it and use a different local port (e.g. 3001).
If you tell me whether you want multiple sites at once (site1, site2, β¦) or just one, Iβll show the cleanest tailscale serve setup (separate port per site vs path-based).
root@srv1100186:~# bash
root@srv1100186:~# cd /docker/openclaw-zs4q
mkdir -p projects/sites/site1
root@srv1100186:/docker/openclaw-zs4q#
Nice β folder created.
Next steps (copy/paste):
1) Put a test page in there
cat > /docker/openclaw-zs4q/projects/sites/site1/index.html <<'EOF'
<!doctype html>
<html>
<head><meta charset="utf-8"><title>site1</title></head>
<body><h1>site1 (tailnet only)</h1></body>
</html>
EOF
2) Add the site1 nginx service + OpenClaw projects mount
Edit compose:
nano /docker/openclaw-zs4q/docker-compose.yml
- In
openclaw:service, make sure you have:
volumes:
- ./data:/data
- ./data/linuxbrew:/home/linuxbrew
- ./projects:/projects
- Add this new service (same indentation level as
openclaw:):
site1:
image: nginx:alpine
restart: unless-stopped
volumes:
- ./projects/sites/site1:/usr/share/nginx/html:ro
ports:
- "127.0.0.1:3000:80"
Apply:
docker compose up -d
Verify local:
curl -I http://127.0.0.1:3000
3) Publish via Tailscale Serve (HTTPS on a different port)
(OpenClaw is already on 443, so use 8443 for the website)
sudo tailscale serve --bg --https=8443 http://127.0.0.1:3000
tailscale serve status
Then open on your Tailnet device:
https://srv1100186.tail777ba9.ts.net:8443/
If you want, paste your updated docker-compose.yml and Iβll sanity-check indentation before you restart anything.
Last login: Sun Feb 15 06:10:18 2026 from 169.254.0.1
root@srv1100186:~# bash
root@srv1100186:~# cd /docker/openclaw-zs4q
mkdir -p projects/sites/site1
root@srv1100186:/docker/openclaw-zs4q# cat > /docker/openclaw-zs4q/projects/sites/site1/index.html <<'EOF'
<!doctype html>
<html>
<head><meta charset="utf-8"><title>site1</title></head>
<body><h1>site1 (tailnet only)</h1></body>
</html>
EOF
root@srv1100186:/docker/openclaw-zs4q#
Great β file is in place.
Now do the compose + serve steps:
1) Edit /docker/openclaw-zs4q/docker-compose.yml
nano /docker/openclaw-zs4q/docker-compose.yml
Make sure you have (under services:):
services:
openclaw:
# ...
volumes:
- ./data:/data
- ./data/linuxbrew:/home/linuxbrew
- ./projects:/projects
site1:
image: nginx:alpine
restart: unless-stopped
volumes:
- ./projects/sites/site1:/usr/share/nginx/html:ro
ports:
- "127.0.0.1:3000:80"
2) Apply + verify local
docker compose up -d
curl -I http://127.0.0.1:3000
3) Expose it on Tailnet (HTTPS)
(Use 8443 so it doesnβt conflict with your OpenClaw serve on 443)
sudo tailscale serve --bg --https=8443 http://127.0.0.1:3000
tailscale serve status
Open from your Tailnet device:
https://srv1100186.tail777ba9.ts.net:8443/
If docker compose up -d errors (YAML/indent), paste the compose file and Iβll spot the issue quickly.
GNU nano 7.2 /docker/openclaw-zs4q/docker-compose.yml
services:
openclaw:
image: ghcr.io/hostinger/hvps-openclaw:latest
init: true
ports:
- "127.0.0.1:${PORT}:${PORT}"
env_file:
- .env
restart: unless-stopped
volumes:
- ./data:/data
- ./data/linuxbrew:/home/linuxbrew
Youβre in the right file. Just add one more volume under openclaw: and then add the new site1 service.
In nano, update it to look like this (exact indentation):
services:
openclaw:
image: ghcr.io/hostinger/hvps-openclaw:latest
init: true
ports:
- "127.0.0.1:${PORT}:${PORT}"
env_file:
- .env
restart: unless-stopped
volumes:
- ./data:/data
- ./data/linuxbrew:/home/linuxbrew
- ./projects:/projects
site1:
image: nginx:alpine
restart: unless-stopped
volumes:
- ./projects/sites/site1:/usr/share/nginx/html:ro
ports:
- "127.0.0.1:3000:80"
Save + exit: Ctrl+O, Enter, then Ctrl+X.
Then run:
docker compose up -d
curl -I http://127.0.0.1:3000
sudo tailscale serve --bg --https=8443 http://127.0.0.1:3000
tailscale serve status
If docker compose up -d complains, paste the full compose file and Iβll fix the YAML.
is there a easy way to replace the text in that block?
Yep β easiest is: donβt replace the whole block, just insert 2 additions.
In nano (fast)
1) Add one line under volumes: in the openclaw service
Put your cursor under:
- ./data/linuxbrew:/home/linuxbrew
and add:
- ./projects:/projects
2) Append the site1 service at the bottom of the file
Go to end (Ctrl+_ then enter a big line number, or just arrow down), then paste:
site1:
image: nginx:alpine
restart: unless-stopped
volumes:
- ./projects/sites/site1:/usr/share/nginx/html:ro
ports:
- "127.0.0.1:3000:80"
Nano shortcuts that help:
- Delete a whole line:
Ctrl+K - Paste: right-click in terminal (or
Shift+Insert) - Save:
Ctrl+Othen Enter - Exit:
Ctrl+X
If you want, paste the full file after your edits (no secrets) and Iβll sanity-check indentation before you run docker compose up -d.
Last login: Sun Feb 15 06:10:18 2026 from 169.254.0.1
root@srv1100186:~# bash
root@srv1100186:~# cd /docker/openclaw-zs4q
mkdir -p projects/sites/site1
root@srv1100186:/docker/openclaw-zs4q# cat > /docker/openclaw-zs4q/projects/sites/site1/index.html <<'EOF'
<!doctype html>
<html>
<head><meta charset="utf-8"><title>site1</title></head>
<body><h1>site1 (tailnet only)</h1></body>
</html>
EOF
root@srv1100186:/docker/openclaw-zs4q# nano /docker/openclaw-zs4q/docker-compose.yml
root@srv1100186:/docker/openclaw-zs4q# docker compose up -d
curl -I http://127.0.0.1:3000
sudo tailscale serve --bg --https=8443 http://127.0.0.1:3000
tailscale serve status
yaml: unmarshal errors:
line 16: mapping key "image" already defined at line 3
line 20: mapping key "ports" already defined at line 5
line 17: mapping key "restart" already defined at line 9
line 18: mapping key "volumes" already defined at line 10
curl: (7) Failed to connect to 127.0.0.1 port 3000 after 0 ms: Couldn't connect to server
Available within your tailnet:
https://srv1100186.tail777ba9.ts.net:8443/
|-- proxy http://127.0.0.1:3000
Serve started and running in the background.
To disable the proxy, run: tailscale serve --https=8443 off
https://srv1100186.tail777ba9.ts.net (tailnet only)
|-- / proxy http://127.0.0.1:43710
https://srv1100186.tail777ba9.ts.net:8443 (tailnet only)
|-- / proxy http://127.0.0.1:3000
root@srv1100186:/docker/openclaw-zs4q#
Those YAML errors mean your site1 block ended up inside the openclaw: map (or at the wrong indentation), so keys like image: / ports: / volumes: are now defined twice.
0) (Optional) turn off the broken 8443 proxy for now
Right now Tailscale is proxying to 127.0.0.1:3000 but nothing is listening.
sudo tailscale serve --https=8443 off
1) Show your current compose file with line numbers
Run:
nl -ba /docker/openclaw-zs4q/docker-compose.yml | sed -n '1,120p'
Paste the output here and Iβll point to the exact bad lines.
2) What the file should look like (structure + indentation)
Make sure site1: is a sibling of openclaw: under services::
services:
openclaw:
image: ghcr.io/hostinger/hvps-openclaw:latest
init: true
ports:
- "127.0.0.1:${PORT}:${PORT}"
env_file:
- .env
restart: unless-stopped
volumes:
- ./data:/data
- ./data/linuxbrew:/home/linuxbrew
- ./projects:/projects
site1:
image: nginx:alpine
restart: unless-stopped
volumes:
- ./projects/sites/site1:/usr/share/nginx/html:ro
ports:
- "127.0.0.1:3000:80"
After you fix it, validate + start:
docker compose config
docker compose up -d
curl -I http://127.0.0.1:3000
Then re-enable Tailnet link:
sudo tailscale serve --bg --https=8443 http://127.0.0.1:3000
tailscale serve status
root@srv1100186:/docker/openclaw-zs4q# nl -ba /docker/openclaw-zs4q/docker-compose.yml | sed -n '1,120p'
1 services:
2 openclaw:
3 image: ghcr.io/hostinger/hvps-openclaw:latest
4 init: true
5 ports:
6 - "127.0.0.1:${PORT}:${PORT}"
7 env_file:
8 - .env
9 restart: unless-stopped
10 volumes:
11 - ./data:/data
12 - ./data/linuxbrew:/home/linuxbrew
13 - ./projects:/projects
14
15 site1:
16 image: nginx:alpine
17 restart: unless-stopped
18 volumes:
19 - ./projects/sites/site1:/usr/share/nginx/html:ro
20 ports:
21 - "127.0.0.1:3000:80"
root@srv1100186:/docker/openclaw-zs4q#
Yep β itβs 100% indentation. Right now everything is at the same indentation level, so YAML thinks openclaw, image, ports, etc. are all top-level keys, and it breaks.
Your file should be:
services:
openclaw:
image: ghcr.io/hostinger/hvps-openclaw:latest
init: true
ports:
- "127.0.0.1:${PORT}:${PORT}"
env_file:
- .env
restart: unless-stopped
volumes:
- ./data:/data
- ./data/linuxbrew:/home/linuxbrew
- ./projects:/projects
site1:
image: nginx:alpine
restart: unless-stopped
volumes:
- ./projects/sites/site1:/usr/share/nginx/html:ro
ports:
- "127.0.0.1:3000:80"
Quick fix (on the VPS)
- Edit:
nano /docker/openclaw-zs4q/docker-compose.yml
-
Add two spaces before
openclaw:andsite1:, and four spaces before their inner keys, and six spaces before list items (- ...) like in the example above. -
Validate:
docker compose -f /docker/openclaw-zs4q/docker-compose.yml config
- Start:
docker compose up -d
curl -I http://127.0.0.1:3000
Then you can re-enable the Tailscale link if you turned it off:
sudo tailscale serve --bg --https=8443 http://127.0.0.1:3000
GNU nano 7.2 /docker/openclaw-zs4q/docker-compose.yml *
services:
openclaw:
image: ghcr.io/hostinger/hvps-openclaw:latest
init: true
ports:
-
"127.0.0.1:${PORT}:${PORT}"
env_file: -
.env
restart: unless-stopped
volumes: -
./data:/data
-
./data/linuxbrew:/home/linuxbrew
-
./projects:/projects
site1:
image: nginx:alpine
restart: unless-stopped
volumes: -
./projects/sites/site1:/usr/share/nginx/html:ro
ports: -
"127.0.0.1:3000:80"
like this?
Almost β you got openclaw: and site1: indented correctly, but everything inside them still needs indentation.
Rule of thumb:
services:starts at column 1- service names (
openclaw:,site1:) = 2 spaces - keys inside a service (
image:,ports:β¦) = 4 spaces - list items (
- ...) underports/env_file/volumes= 6 spaces
So in nano it should look exactly like:
services:
openclaw:
image: ghcr.io/hostinger/hvps-openclaw:latest
init: true
ports:
- "127.0.0.1:${PORT}:${PORT}"
env_file:
- .env
restart: unless-stopped
volumes:
- ./data:/data
- ./data/linuxbrew:/home/linuxbrew
- ./projects:/projects
site1:
image: nginx:alpine
restart: unless-stopped
volumes:
- ./projects/sites/site1:/usr/share/nginx/html:ro
ports:
- "127.0.0.1:3000:80"
After saving, run (this will tell you immediately if indentation is fixed):
docker compose -f /docker/openclaw-zs4q/docker-compose.yml config
TZ: Europe/Berlin
image: ghcr.io/hostinger/hvps-openclaw:latest
init: true
networks:
default: null
ports:
- mode: ingress
host_ip: 127.0.0.1
target: 43710
published: "43710"
protocol: tcp
restart: unless-stopped
volumes:
- type: bind
source: /docker/openclaw-zs4q/data
target: /data
bind: {}
- type: bind
source: /docker/openclaw-zs4q/data/linuxbrew
target: /home/linuxbrew
bind: {}
- type: bind
source: /docker/openclaw-zs4q/projects
target: /projects
bind: {}
site1:
image: nginx:alpine
networks:
default: null
ports:
- mode: ingress
host_ip: 127.0.0.1
target: 80
published: "3000"
protocol: tcp
restart: unless-stopped
volumes:
- type: bind
source: /docker/openclaw-zs4q/projects/sites/site1
target: /usr/share/nginx/html
read_only: true
bind: {}
networks:
default:
name: openclaw-zs4q_default
root@srv1100186:/docker/openclaw-zs4q#
Perfect β docker compose ... config output means your YAML is valid now.
Now bring it up and test:
docker compose up -d
curl -I http://127.0.0.1:3000
If that returns HTTP/1.1 200 OK, re-enable (or keep) the Tailnet HTTPS link:
sudo tailscale serve --bg --https=8443 http://127.0.0.1:3000
tailscale serve status
Then open from a Tailnet device:
https://srv1100186.tail777ba9.ts.net:8443/
If curl fails, paste:
docker ps --format "table {{.Names}}\t{{.Ports}}"
docker logs --tail=50 site1 2>/dev/null || docker logs --tail=50 openclaw-zs4q-site1-1
root@srv1100186:/docker/openclaw-zs4q# docker compose up -d
curl -I http://127.0.0.1:3000
[+] up 13/13
β Image nginx:alpine Pulled 3.0s
β Container openclaw-zs4q-site1-1 Created 0.1s
β Container openclaw-zs4q-openclaw-1 Recreated 2.4s
curl: (56) Recv failure: Connection reset by peer
root@srv1100186:/docker/openclaw-zs4q#
site1 container got created, so the compose is fine β but curl getting βConnection reset by peerβ means something is accepting the TCP connection on 127.0.0.1:3000 and then immediately closing it (most likely the site1 container crashing / restarting).
Run these on the VPS and paste the output:
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
docker logs --tail=100 openclaw-zs4q-site1-1
docker inspect -f '{{.State.Status}} exit={{.State.ExitCode}} err={{.State.Error}}' openclaw-zs4q-site1-1
curl -v http://127.0.0.1:3000/
Also, since 8443 is currently proxying to a broken backend, you can temporarily turn that off to reduce confusion (optional):
sudo tailscale serve --https=8443 off
Once we see the docker logs for openclaw-zs4q-site1-1, itβll usually be obvious whatβs causing the reset.
< Last-Modified: Sun, 15 Feb 2026 07:06:54 GMT
< Connection: keep-alive
< ETag: "6991708e-82"
< Accept-Ranges: bytes
<
<!doctype html>
<html>
<head><meta charset="utf-8"><title>site1</title></head>
<body><h1>site1 (tailnet only)</h1></body>
</html>
- Connection #0 to host 127.0.0.1 left intact
root@srv1100186:/docker/openclaw-zs4q#
Nice β that means itβs actually working now. The earlier βconnection resetβ was likely just during container start/recreate (nginx not ready for a split second).
Now you should be able to access it over Tailnet too.
1) Re-enable / confirm Tailscale Serve for the site
sudo tailscale serve --bg --https=8443 http://127.0.0.1:3000
tailscale serve status
2) Open from a Tailnet device
Go to:
https://srv1100186.tail777ba9.ts.net:8443/
Next: let OpenClaw edit the site
Tell OpenClaw (in the Control UI chat) something like:
βEdit
/projects/sites/site1/index.htmland create a simple homepage with CSS, a navbar, and an About section.β
If you want multiple sites later, we can do site2 on port 3001 and serve it on another HTTPS port (8444), or use path-based routing.
root@srv1100186:/docker/openclaw-zs4q# sudo tailscale serve --bg --https=8443 http://127.0.0.1:3000
tailscale serve status
Available within your tailnet:
https://srv1100186.tail777ba9.ts.net:8443/
|-- proxy http://127.0.0.1:3000
Serve started and running in the background.
To disable the proxy, run: tailscale serve --https=8443 off
https://srv1100186.tail777ba9.ts.net (tailnet only)
|-- / proxy http://127.0.0.1:43710
https://srv1100186.tail777ba9.ts.net:8443 (tailnet only)
|-- / proxy http://127.0.0.1:3000
root@srv1100186:/docker/openclaw-zs4q#
Perfect β you now have:
- OpenClaw UI (tailnet-only): https://srv1100186.tail777ba9.ts.net/ β
127.0.0.1:43710 - Website (tailnet-only): https://srv1100186.tail777ba9.ts.net:8443/ β
127.0.0.1:3000
Next, in the OpenClaw Control UI, just tell it to edit files in:
/projects/sites/site1/ (e.g. index.html, add style.css, about.html, etc.)
If this setup is what you wanted, please mark the solution so itβs indexed (rightβclick β Apps β β Mark Solution).
Let's say I want to create sites for local businesses and email them the final design created by openclaw how can I make them publically accessible on a temporary domain so they can pay and get the site working?
Yes β the βquick temporary public preview linkβ version is Tailscale Funnel.
Right now your site is tailnet-only via tailscale serve on :8443. Funnel is the same idea, but public internet.
Option 1: Fastest public preview (Tailscale Funnel)
On the VPS:
- Make sure the site works locally first:
curl -I http://127.0.0.1:3000
- Turn off the tailnet-only Serve on 8443 (so Funnel can take over that port):
sudo tailscale serve --https=8443 off
- Enable Funnel on 8443 (public):
sudo tailscale funnel --bg --https=8443 http://127.0.0.1:3000
- Check:
tailscale funnel status
Now you can send the client a link like:
https://srv1100186.<your-tailnet>.ts.net:8443/
(Yes itβs a βtemporary domainβ, and yes itβs public.)
Important notes
- Funnel can only use ports 443 / 8443 / 10000.
- Donβt Funnel your OpenClaw UI (keep that tailnet-only).
Add a βguess-hardβ URL per client (cheap protection)
You can put the preview behind a unique path:
sudo tailscale funnel --bg --https=8443 --set-path=/acme-preview-7f3a http://127.0.0.1:3000
Then share:
https://...:8443/acme-preview-7f3a/
(Not real security, but helps avoid random discovery.)
If you need many client previews at once
Funnel is limited (only a couple public ports). For an agency-style workflow, the scalable approach is:
- buy a domain (e.g.
yourstudio.com) - run Caddy/Nginx on the VPS (public 80/443)
- give each client
clientname-preview.yourstudio.comwith Basic Auth
If you tell me β1 client at a timeβ vs βmany concurrentlyβ, Iβll steer you to the right setup.
Done β I created a βcoolβ interactive mini-site in:
/projects/sites/site1/
Files included:
index.html (interactive playground: live preview + shuffle + copy-to-clipboard + theme toggle)
about.html (explainer page + clickable βfocus areaβ chips)
style.css (dark/light theme + layout)
script.js (all interactivity + localStorage persistence)
README.txt
On the VPS itβs here:
/data/.openclaw/workspace/projects/sites/site1/
How to view quickly (from that folder on any machine):
python -m http.server 8080
Then open:
http://localhost:8080/projects/sites/site1/ (if you run it from workspace root), or
if you cd into site1 first, just http://localhost:8080/
If you tell me how youβre exposing/serving /projects (Tailscale Serve, Nginx, etc.), I can give you the exact URL path youβll be able to open from your laptop.
I let openclaw create something but it's not visible yet
Itβs not visible because your web server is serving a different folder than the one OpenClaw wrote to.
- Your
site1nginx container serves (host):
/docker/openclaw-zs4q/projects/sites/site1/ - OpenClaw created the site in (host):
/docker/openclaw-zs4q/data/.openclaw/workspace/projects/sites/site1/
So nginx is still serving the βoldβ/empty folder.
Fix (copy the generated files into the served folder)
Run on the VPS:
ls -la /docker/openclaw-zs4q/data/.openclaw/workspace/projects/sites/site1/
Then sync/copy into the folder nginx serves:
rsync -a --delete \
/docker/openclaw-zs4q/data/.openclaw/workspace/projects/sites/site1/ \
/docker/openclaw-zs4q/projects/sites/site1/
Test locally:
curl http://127.0.0.1:3000/ | head
Then open your Tailnet URL again:
https://srv1100186.tail777ba9.ts.net:8443/
(Optional) Make OpenClaw write to the right place next time
In your prompts, explicitly tell it: βWrite the site to /projects/sites/site1/β (that path is your mounted shared folder).
If you want, paste ls -la /docker/openclaw-zs4q/projects/sites/site1 after the rsync and Iβll confirm nginx is seeing the new files.
It works now can I make sure somehow openclaw remembers this? should we let it create a .md file?
Yes β best way is to βremember via files + workspaceβ, not by hoping the model recalls it later.
Option A (simplest): have OpenClaw write a README.md in the served folder
Ask OpenClaw to create:
/projects/sites/site1/README.md
Include notes like:
- Final output directory:
/projects/sites/site1/(this is what nginx serves) - If OpenClaw creates stuff under
/data/.openclaw/workspace/..., copy it over with:
rsync -a --delete /docker/openclaw-zs4q/data/.openclaw/workspace/projects/sites/site1/ /docker/openclaw-zs4q/projects/sites/site1/
- Preview URLs:
- local:
http://127.0.0.1:3000/ - tailnet:
https://srv1100186.tail777ba9.ts.net:8443/
Then, in future prompts, start with: βBefore changes, read /projects/sites/site1/README.md and follow it.β
Option B (better): set OpenClawβs default workspace to /projects
So it naturally creates files in the right place.
On the VPS:
docker exec -it openclaw-zs4q-openclaw-1 openclaw config set agents.defaults.workspace "/projects"
docker restart openclaw-zs4q-openclaw-1
After that, OpenClaw should default to writing under /projects/....
If you want, paste what docker exec ... openclaw config get agents.defaults.workspace shows after setting it, and Iβll confirm it stuck.
root@srv1100186:/docker/openclaw-zs4q# docker exec -it openclaw-zs4q-openclaw-1 openclaw config set agents.defaults.workspace "/projects"
docker restart openclaw-zs4q-openclaw-1
π¦ OpenClaw 2026.2.12 (f9e444d) β Hot reload for config, cold sweat for deploys.
Updated agents.defaults.workspace. Restart the gateway to apply.
openclaw-zs4q-openclaw-1
root@srv1100186:/docker/openclaw-zs4q#
so now it will write automatically to there?
Mostly yes: setting agents.defaults.workspace means new runs will default to using /projects as the working directory, so when you say βcreate files in ./sites/site1β it should land under:
/projects/sites/site1/
A couple gotchas to be aware of:
- Verify it actually took effect
Run:
docker exec -it openclaw-zs4q-openclaw-1 openclaw config get agents.defaults.workspace
It should print /projects.
- Prompts still matter
Some coding flows/tools might still pick an explicit path (or reuse an older path in the session). To be safe, start your prompts with something like:
βWork in
/projects/sites/site1/(this is the folder nginx serves). Donβt write to/data/.openclaw/workspace.β
- Existing sessions
If you keep using an old session/thread that already βdecidedβ on a different folder, it might continue there. Starting a fresh chat/session after the restart helps.
If you paste the output of the config get command above, Iβll confirm itβs set correctly.
Updated agents.defaults.workspace. Restart the gateway to apply.
openclaw-zs4q-openclaw-1
root@srv1100186:/docker/openclaw-zs4q# docker exec -it openclaw-zs4q-openclaw-1 openclaw config get agents.defaults.workspace
π¦ OpenClaw 2026.2.12 (f9e444d) β Welcome to the command line: where dreams compile and confidence segfaults.
/projects
root@srv1100186:/docker/openclaw-zs4q#
Yep β that confirms itβs set. New runs should default to /projects as the workspace.
Practical way to use it now:
- Say: βCreate/update the site in
sites/site1/β
(it should resolve to/projects/sites/site1/)
Iβd still recommend one extra guardrail: add a /projects/README.md (or /projects/sites/site1/README.md) that states βthis folder is served by nginx + tailscaleβ, and in your prompt tell OpenClaw: βRead the README first and follow it.β
Also: youβre on OpenClaw 2026.2.12 and your earlier audit showed an update available (2026.2.14). When you have a moment, updating is worth it for fixes/security, but donβt do it mid-project unless things are stable.
should we give openclaw a prompt so it understands this? can it also code more complex things in here?
Yes β giving it a standard βproject promptβ helps a lot. And yes, it can build more complex things, but youβll want to be explicit about where to write files, how to run/serve, and not exposing anything publicly.
A good βdefault promptβ to paste into OpenClaw
Paste this once at the top of a new chat/session:
Project rules (read carefully):
- Workspace is
/projects(this is a shared, persistent volume).- For this job, work only in
/projects/sites/site1/.- This folder is served by nginx on
http://127.0.0.1:3000/and exposed tailnet-only athttps://srv1100186.tail777ba9.ts.net:8443/.- Do not write to
/data/.openclaw/workspace/...and do not create files outside/projects.- Prefer static files (HTML/CSS/JS). If you need a build system (React/Next/etc), ask first and explain how it will be served.
- After changes, summarize what files changed and what URL to test.
That will βgroundβ it.
Can it code more complex things here?
Works great:
- multi-page static sites (HTML/CSS/JS)
- forms with client-side validation
- small βappsβ (quiz, booking-request form that emails via a 3rd party API, etc.)
- simple backend APIs if you add another container/service (Node/Express, Python/FastAPI)
Needs extra setup / decisions:
- React/Next/Vite (build step + dev server vs static export)
- databases (Postgres/SQLite), auth, admin panels
- sending email (SMTP provider/API keys β treat as secrets)
If you tell me what βmore complexβ means (e.g. βbooking form that emails meβ, βCMSβ, βloginβ, βpaymentsβ), Iβll recommend the cleanest stack that still fits your Docker + Tailnet setup.
Error: EACCES: permission denied, open '/projects/AGENTS.md'
shows this in the UI now
That error means the OpenClaw process canβt write to the /projects volume (usually because the folder on the VPS is owned by root:root with 755, while the container runs as a non-root user).
1) Find what user OpenClaw runs as (inside the container)
Run on the VPS:
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'whoami; id; ls -ld /projects'
2) Fix ownership of the host folder to match that UID/GID
This βjust worksβ approach grabs the UID/GID from inside the container and applies it to the host directory:
uid=$(docker exec openclaw-zs4q-openclaw-1 id -u)
gid=$(docker exec openclaw-zs4q-openclaw-1 id -g)
sudo chown -R "$uid:$gid" /docker/openclaw-zs4q/projects
sudo chmod -R u+rwX /docker/openclaw-zs4q/projects
3) Try again
Refresh the UI and retry creating /projects/AGENTS.md (or whatever it was doing).
If you still hit permissions issues, paste the output of step (1) and Iβll tell you exactly whatβs wrong (sometimes itβs just that only a subfolder needs fixing).
π¦ OpenClaw 2026.2.12 (f9e444d) β Hot reload for config, cold sweat for deploys.
Updated agents.defaults.workspace. Restart the gateway to apply.
openclaw-zs4q-openclaw-1
root@srv1100186:/docker/openclaw-zs4q# docker exec -it openclaw-zs4q-openclaw-1 openclaw config get agents.defaults.workspace
π¦ OpenClaw 2026.2.12 (f9e444d) β Welcome to the command line: where dreams compile and confidence segfaults.
/projects
root@srv1100186:/docker/openclaw-zs4q# docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'whoami; id; ls -ld /projects'
root
uid=0(root) gid=0(root) groups=0(root)
drwxr-xr-x 3 root root 4096 Feb 15 08:06 /projects
root@srv1100186:/docker/openclaw-zs4q# uid=$(docker exec openclaw-zs4q-openclaw-1 id -u)
gid=$(docker exec openclaw-zs4q-openclaw-1 id -g)
sudo chown -R "$uid:$gid" /docker/openclaw-zs4q/projects
root@srv1100186:/docker/openclaw-zs4q# sudo chmod -R u+rwX /docker/openclaw-zs4q/projects
root@srv1100186:/docker/openclaw-zs4q#
Error: EACCES: permission denied, open '/projects/AGENTS.md'
That EACCES means the OpenClaw process user canβt write to the bind-mounted /projects folder. The confusing bit: docker exec defaults to running as root, so whoami there doesnβt tell us what user the gateway process is running as.
1) Check what user the gateway (PID 1) runs as
Run on the VPS:
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'ps -o user=,uid=,gid=,cmd= -p 1; ls -ldn /projects'
2) Chown the host projects folder to that UID:GID
Replace UID/GID with what you saw above (common is 1000:1000):
sudo chown -R UID:GID /docker/openclaw-zs4q/projects
sudo chmod -R u+rwX /docker/openclaw-zs4q/projects
3) Quick write test as that same UID inside the container
docker exec -u UID:GID -it openclaw-zs4q-openclaw-1 sh -lc 'echo ok > /projects/_write_test && ls -l /projects/_write_test'
After that, OpenClaw should be able to create /projects/AGENTS.md (and any site files) normally.
If you paste the output of step (1), Iβll tell you the exact chown + docker exec -u ... values to use.
root@srv1100186:/docker/openclaw-zs4q# docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'ps -o user=,uid=,gid=,cmd= -p 1; ls -ldn /projects'
root 0 0 /sbin/docker-init -- /entrypoint.sh node server.mjs
drwxr-xr-x 3 0 0 4096 Feb 15 08:06 /projects
root@srv1100186:/docker/openclaw-zs4q#
That error is filesystem permissions, not a βpromptβ problem.
Even though the container looks like itβs running as root, your Docker setup may be using user-namespace remapping (common on managed VPS images). In that case, βroot in the containerβ maps to some other UID on the host, and chown 0:0 on the host wonβt help.
Fix: make /projects owned like /data (since /data already works)
Run on the VPS:
# See what UID/GID owns the working /data mount on the host:
stat -c 'data owner=%u:%g perms=%A %n' /docker/openclaw-zs4q/data
stat -c 'proj owner=%u:%g perms=%A %n' /docker/openclaw-zs4q/projects
# Copy the ownership from /data β /projects
uid=$(stat -c %u /docker/openclaw-zs4q/data)
gid=$(stat -c %g /docker/openclaw-zs4q/data)
sudo chown -R "$uid:$gid" /docker/openclaw-zs4q/projects
sudo chmod -R u+rwX /docker/openclaw-zs4q/projects
Then test from inside the container:
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'echo ok > /projects/_write_test && ls -l /projects/_write_test'
If that succeeds, the UI should stop throwing EACCES.
Strong recommendation (keeps things tidy)
Donβt use /projects itself as the workspace root (itβll try to put AGENTS.md, memory/, etc. next to your sites). Use a subfolder:
mkdir -p /docker/openclaw-zs4q/projects/_openclaw-workspace
docker exec -it openclaw-zs4q-openclaw-1 openclaw config set agents.defaults.workspace "/projects/_openclaw-workspace"
docker restart openclaw-zs4q-openclaw-1
If you paste the two stat ... lines output, I can tell you if userns-remap is definitely whatβs happening.
π¦ OpenClaw 2026.2.12 (f9e444d) β Welcome to the command line: where dreams compile and confidence segfaults.
/projects
root@srv1100186:/docker/openclaw-zs4q# docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'whoami; id; ls -ld /projects'
root
uid=0(root) gid=0(root) groups=0(root)
drwxr-xr-x 3 root root 4096 Feb 15 08:06 /projects
root@srv1100186:/docker/openclaw-zs4q# uid=$(docker exec openclaw-zs4q-openclaw-1 id -u)
gid=$(docker exec openclaw-zs4q-openclaw-1 id -g)
sudo chown -R "$uid:$gid" /docker/openclaw-zs4q/projects
root@srv1100186:/docker/openclaw-zs4q# sudo chmod -R u+rwX /docker/openclaw-zs4q/projects
root@srv1100186:/docker/openclaw-zs4q# docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'ps -o user=,uid=,gid=,cmd= -p 1; ls -ldn /projects'
root 0 0 /sbin/docker-init -- /entrypoint.sh node server.mjs
drwxr-xr-x 3 0 0 4096 Feb 15 08:06 /projects
root@srv1100186:/docker/openclaw-zs4q# # See what UID/GID owns the working /data mount on the host:
stat -c 'data owner=%u:%g perms=%A %n' /docker/openclaw-zs4q/data
stat -c 'proj owner=%u:%g perms=%A %n' /docker/openclaw-zs4q/projects
Copy the ownership from /data β /projects
uid=$(stat -c %u /docker/openclaw-zs4q/data)
gid=$(stat -c %g /docker/openclaw-zs4q/data)
sudo chown -R "$uid:$gid" /docker/openclaw-zs4q/projects
data owner=1000:1000 perms=drwxr-xr-x /docker/openclaw-zs4q/data
proj owner=0:0 perms=drwxr-xr-x /docker/openclaw-zs4q/projects
root@srv1100186:/docker/openclaw-zs4q# sudo chmod -R u+rwX /docker/openclaw-zs4q/projects
root@srv1100186:/docker/openclaw-zs4q# docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'echo ok > /projects/_write_test && ls -l /projects/_write_test'
-rw-r--r-- 1 root root 3 Feb 15 08:46 /projects/_write_test
root@srv1100186:/docker/openclaw-zs4q#
Good news: your write test succeeded (/projects/_write_test got created), so the permissions problem is fixed now. OpenClaw should stop throwing EACCES once you refresh/retry.
Two follow-ups Iβd do:
1) Confirm /projects is now owned like /data (host-side)
On the VPS:
stat -c 'proj owner=%u:%g perms=%A %n' /docker/openclaw-zs4q/projects
You want it to show 1000:1000 (same as your /data mount).
(Optional cleanup)
rm -f /docker/openclaw-zs4q/projects/_write_test
2) Donβt use /projects as the workspace root (recommended)
If your workspace is /projects, OpenClaw will create AGENTS.md, memory/, etc inside /projects. Thatβs messy, and if you ever make a site public later, you really donβt want any of that near your web content.
Better pattern:
- Workspace:
/projects/_openclaw-workspace - Websites:
/projects/sites/...
Commands (VPS):
mkdir -p /docker/openclaw-zs4q/projects/_openclaw-workspace
sudo chown -R 1000:1000 /docker/openclaw-zs4q/projects/_openclaw-workspace
docker exec -it openclaw-zs4q-openclaw-1 openclaw config set agents.defaults.workspace "/projects/_openclaw-workspace"
docker restart openclaw-zs4q-openclaw-1
Then in your prompts, tell it explicitly: βwrite the website to /projects/sites/site1/β.
If you try again and still see EACCES, paste:
ls -ldn /docker/openclaw-zs4q/projects /docker/openclaw-zs4q/projects/_openclaw-workspace
openclaw-1 sh -lc 'echo ok > /projects/_write_test && ls -l /projects/_write_test'
-rw-r--r-- 1 root root 3 Feb 15 08:46 /projects/_write_test
root@srv1100186:/docker/openclaw-zs4q# stat -c 'proj owner=%u:%g perms=%A %n' /docker/openclaw-zs4q/projects
proj owner=1000:1000 perms=drwxr-xr-x /docker/openclaw-zs4q/projects
root@srv1100186:/docker/openclaw-zs4q# rm -f /docker/openclaw-zs4q/projects/_write_test
root@srv1100186:/docker/openclaw-zs4q# mkdir -p /docker/openclaw-zs4q/projects/_openclaw-workspace
sudo chown -R 1000:1000 /docker/openclaw-zs4q/projects/_openclaw-workspace
docker exec -it openclaw-zs4q-openclaw-1 openclaw config set agents.defaults.workspace "/projects/_openclaw-workspace"
docker restart openclaw-zs4q-openclaw-1
π¦ OpenClaw 2026.2.12 (f9e444d) β WhatsApp automation without the "please accept our new privacy policy".
Updated agents.defaults.workspace. Restart the gateway to apply.
openclaw-zs4q-openclaw-1
root@srv1100186:/docker/openclaw-zs4q#
Perfect β thatβs now the clean setup.
- OpenClaw workspace (AGENTS.md, memory, etc.):
/projects/_openclaw-workspace - Your websites:
/projects/sites/...(served by nginx)
One last quick verify (optional but nice):
docker exec -it openclaw-zs4q-openclaw-1 openclaw config get agents.defaults.workspace
It should print:
/projects/_openclaw-workspace
From now on, when you want OpenClaw to edit the website, tell it explicitly:
βWrite the site files to
/projects/sites/site1/(this is the folder nginx serves).β
If this whole thread solved your setup, please mark the solution (rightβclick β Apps β β Mark Solution) so itβs indexed.
so what prompt to give it?
Paste this as your βstarter promptβ in a new OpenClaw chat (edit the site name as needed):
Project rules (follow exactly):
- Your workspace is
/projects/_openclaw-workspace(AGENTS.md + memory live there).- The website you are building is site1 and must live in:
/projects/sites/site1/(this is what nginx serves).- Do not write website files under
/data/.openclaw/workspaceor anywhere outside/projects.- This site is served locally at
http://127.0.0.1:3000/and tailnet-only athttps://srv1100186.tail777ba9.ts.net:8443/.- Prefer plain HTML/CSS/JS (no build tools). If you think you need React/Vite/Next, ask first and explain why.
- After every change: list the files changed + what to test + the URL to open.
Then your actual task prompt can be something like:
βBuild a 1-page website for a local business (home + services + contact section). Create
index.html,style.css, andscript.jsin/projects/sites/site1/. Make it mobile-friendly and fast.β
If you tell me the business type (restaurant, barber, plumber, etc.), Iβll give you a really good βbriefβ prompt that produces a solid first draft.
Okay cool, so would it possible to upload a project folder on which I've been working already before from my local pc onto there so it can work on it?
Yes β totally. You just need to get your local project folder onto the VPS under /docker/openclaw-zs4q/projects/sites/<name>/ (which maps to /projects/sites/<name>/ inside the container). Then OpenClaw can edit it.
Option A (recommended): scp (simple)
From your local PC:
scp -r /path/to/your-site/ root@<VPS_PUBLIC_IP>:/docker/openclaw-zs4q/projects/sites/site1
If you want it to copy the contents into an existing folder (instead of nesting), do:
scp -r /path/to/your-site/* root@<VPS_PUBLIC_IP>:/docker/openclaw-zs4q/projects/sites/site1/
Option B: rsync (best for repeated updates)
rsync -av --delete /path/to/your-site/ root@<VPS_PUBLIC_IP>:/docker/openclaw-zs4q/projects/sites/site1/
After upload: fix ownership (important)
On the VPS:
sudo chown -R 1000:1000 /docker/openclaw-zs4q/projects
Verify itβs visible
On the VPS:
ls -αα /docker/openclaw-zs4q/projects/sites/site1
curl -I http://127.0.0.1:3000
If you tell me what OS youβre on locally (Windows/macOS/Linux) and how you SSH (key or password), Iβll give you the exact command format (Windows paths can be annoying).
I am using mac OS and have the folder with files on my desktop
On macOS this is easy with rsync (best) or scp (simpler). Iβll assume your folder is on the Desktop.
1) Pick a site folder name on the VPS
Example: site1 (you can change it).
On the VPS, make sure the destination exists:
mkdir -p /docker/openclaw-zs4q/projects/sites/site1
2) Upload from your Mac β VPS
Option A (recommended): rsync (fast, repeatable)
On your Mac (Terminal):
rsync -av --delete "$HOME/Desktop/YourFolderName/" root@<VPS_IP>:/docker/openclaw-zs4q/projects/sites/site1/
Option B: scp
scp -r "$HOME/Desktop/YourFolderName/" root@<VPS_IP>:/docker/openclaw-zs4q/projects/sites/
(Note: this will create /sites/YourFolderName on the VPS.)
3) Fix permissions so OpenClaw can edit it
On the VPS:
chown -R 1000:1000 /docker/openclaw-zs4q/projects/sites/site1
4) Verify
On the VPS:
ls -la /docker/openclaw-zs4q/projects/sites/site1 | head
Then refresh your site URL (https://srv1100186.tail777ba9.ts.net:8443/).
If you tell me:
- the exact folder name on your Desktop, and
- whether you SSH by key or password, and your VPS hostname/IP,
Iβll paste the exact command with no placeholders.
I need to verify my mac os pc as well in the UI how do I do this again?
To βverifyβ your Mac browser again, you need to pair/approve the device (one-time per browser profile). Same flow as before:
1) Open the Control UI (from your Mac)
Use the HTTPS URL:
https://srv1100186.tail777ba9.ts.net/
Youβll see it try to connect and then disconnect / say pairing required.
2) On the VPS: list pending pairing requests
docker exec -it openclaw-zs4q-openclaw-1 openclaw devices list
3) Approve the new request
Copy the requestId shown under βpendingβ, then:
docker exec -it openclaw-zs4q-openclaw-1 openclaw devices approve <requestId>
4) Refresh the UI on your Mac
After approval, reload the page and it should connect.
Notes:
- If
devices listshows no pending requests, refresh the UI page once and rundevices listagain. - Donβt post tokens/passwords here; the
requestIdis safe to share if needed.
now I want to upload this project folder youtube-outreach-crm to the VPS but why do you want to paste it into sites1?
Shouldn't we have a general workfolder?
Yep β youβre right. sites/site1 was just for a website demo. For something like youtube-outreach-crm (a real app/project), you want a general projects folder.
Recommended structure (on the VPS)
Inside /projects (the shared volume):
- Workspace (OpenClaw βbrainβ files):
/projects/_openclaw-workspace/ - Websites:
/projects/sites/<site-name>/ - General code projects/apps:
/projects/repos/<project-name>/(or/projects/apps/...)
So for your case:
/projects/repos/youtube-outreach-crm/
On the VPS (host path):
mkdir -p /docker/openclaw-zs4q/projects/repos/youtube-outreach-crm
Upload from your Mac (into the general folder)
From your Mac:
rsync -av --delete "$HOME/Desktop/youtube-outreach-crm/" \
root@<VPS_IP>:/docker/openclaw-zs4q/projects/repos/youtube-outreach-crm/
Fix permissions so OpenClaw can edit it
On the VPS:
chown -R 1000:1000 /docker/openclaw-zs4q/projects/repos/youtube-outreach-crm
Then tell OpenClaw where to work
In the Control UI:
βWork in
/projects/repos/youtube-outreach-crm/.β
If you want this app to be accessible by a link too, tell me what it is (static web? Node app? Python? needs a DB?) and what port it runs on, and weβll expose it tailnet-only via tailscale serve (same pattern as the website).
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ED25519 key sent by the remote host is
SHA256:IzbeG1rE7zPg9Hg/b8761obwDqUg1DJ7YRU/VHSdCIU.
Please contact your system administrator.
Add correct host key in /Users/dylandevrieze/.ssh/known_hosts to get rid of this message.
Offending ECDSA key in /Users/dylandevrieze/.ssh/known_hosts:7
Host key for 76.13.143.94 has changed and you have requested strict checking.
Host key verification failed.
rsync(45334): error: unexpected end of file
rsync(45334): error: io_read_nonblocking
rsync(45334): error: io_read_buf
rsync(45334): error: io_read_int
rsync(45334): warning: child 45335 exited with status 255
dylandevrieze@MacBook-Air-6 ~ %
That warning is SSH doing its job: the serverβs host key no longer matches what your Mac previously saved for 76.13.143.94. This can be totally normal (VPS reinstalled/reprovisioned), or it can be a real MITM risk. Donβt just blindly delete it unless you verify.
1) Verify the new fingerprint on the VPS (out-of-band)
If you can open the VPS console in Hostinger (or youβre already SSHβd in from another trusted machine), run on the VPS:
sudo ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
Compare the SHA256 fingerprint it prints to what your Mac shows:
SHA256:IzbeG1rE7zPg9Hg/b8761obwDqUg1DJ7YRU/VHSdCIU
If it matches, youβre safe to update known_hosts.
2) Remove the old key entry on your Mac and reconnect
On your Mac:
ssh-keygen -R 76.13.143.94
ssh root@76.13.143.94
It will prompt to trust the new key; accept it.
(Alternative since it mentions βOffending ECDSA key β¦ line 7β: you can edit ~/.ssh/known_hosts and delete line 7, but ssh-keygen -R is cleaner.)
3) Retry rsync
After SSH works again, your rsync command will work too.
If you want, paste the output of the VPS ssh-keygen -lf ... command (just the fingerprint line).
I am connected to my VPS and now?
Cool β since youβre on the VPS now, first we verify what the correct SSH host key fingerprint should be.
1) On the VPS: print the SSH host key fingerprint
Run:
sudo ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
It should print a SHA256 fingerprint. Compare it to what your Mac showed:
SHA256:IzbeG1rE7zPg9Hg/b8761obwDqUg1DJ7YRU/VHSdCIU
- If it matches: youβre safe to update your Macβs
known_hosts. - If it does NOT match: stop β donβt proceed (possible MITM / wrong server / IP reuse). Use Hostinger console to confirm the IP is really your VPS.
2) On your Mac: remove the old key and reconnect
On your Mac terminal:
ssh-keygen -R 76.13.143.94
ssh root@76.13.143.94
Accept the new key when prompted.
3) Retry your upload (rsync)
Then:
rsync -av --delete "$HOME/Desktop/youtube-outreach-crm/" \
root@76.13.143.94:/docker/openclaw-zs4q/projects/repos/youtube-outreach-crm/
If you paste the output of the VPS ssh-keygen -lf ... command (just that line), Iβll confirm whether it matches the Mac warning fingerprint.
sent 23 bytes received 12 bytes 2.12 bytes/sec
total size is 0 speedup is 0.00
rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1338) [sender=3.2.7]
root@srv1100186:~#
rsync exit code 23 just means βsome files didnβt transferβ β the real reason is in the lines above that output (the βprevious errorsβ it mentions).
One super common cause here: you accidentally ran rsync on the VPS (your prompt shows root@srv...#). But your source folder is on your Mac Desktop, so the upload command must be run on the Mac, not the VPS.
Do this instead
1) On the VPS (destination folder)
mkdir -p /docker/openclaw-zs4q/projects/repos/youtube-outreach-crm
chown -R 1000:1000 /docker/openclaw-zs4q/projects/repos/youtube-outreach-crm
2) On your Mac (upload)
Run this in macOS Terminal:
rsync -av --progress --delete \
"$HOME/Desktop/youtube-outreach-crm/" \
root@76.13.143.94:/docker/openclaw-zs4q/projects/repos/youtube-outreach-crm/
(If youβd rather use Tailscale instead of the public IP, use root@srv1100186.tail777ba9.ts.net:....)
3) Verify on the VPS
ls -la /docker/openclaw-zs4q/projects/repos/youtube-outreach-crm | head
If it still errors
Re-run on your Mac with extra verbosity and paste only the error lines:
rsync -avv --progress "$HOME/Desktop/youtube-outreach-crm/" root@76.13.143.94:/docker/openclaw-zs4q/projects/repos/youtube-outreach-crm/
Copy/paste the lines that contain rsync: / failed: / Permission denied / No such file or directory.
root@srv1100186:~# ls -la /docker/openclaw-zs4q/projects/repos/youtube-outreach-crm | head
total 8
drwxr-xr-x 2 ubuntu ubuntu 4096 Feb 15 08:03 .
drwxr-xr-x 3 root root 4096 Feb 15 08:03 ..
root@srv1100186:~#
That folder is still empty (only . and ..), which means the upload hasnβt actually happened yet.
The key point: the upload command must be run on your Mac, because the source folder is on your Macβs Desktop. If you run rsync on the VPS, it has nothing to send.
1) On the VPS: keep the destination folder
You already have it:
/docker/openclaw-zs4q/projects/repos/youtube-outreach-crm/
(Optional, make sure itβs writable by OpenClaw later)
sudo chown -R 1000:1000 /docker/openclaw-zs4q/projects/repos/youtube-outreach-crm
2) On your Mac: run rsync (this is the actual upload)
In macOS Terminal:
rsync -av --progress --delete \
"$HOME/Desktop/youtube-outreach-crm/" \
root@76.13.143.94:/docker/openclaw-zs4q/projects/repos/youtube-outreach-crm/
If itβs a Node project (recommended excludes)
This avoids copying node_modules, build output, etc.:
rsync -av --progress --delete \
--exclude node_modules --exclude .git --exclude dist --exclude build --exclude .next \
"$HOME/Desktop/youtube-outreach-crm/" \
root@76.13.143.94:/docker/openclaw-zs4q/projects/repos/youtube-outreach-crm/
3) Verify on the VPS
ls -la /docker/openclaw-zs4q/projects/repos/youtube-outreach-crm | head
After that, inside OpenClaw, tell it to work in:
/projects/repos/youtube-outreach-crm/
If your project folder on Desktop has a different name (spaces etc.), tell me the exact name and Iβll paste the exact rsync command.
sent 23 bytes received 12 bytes 5.38 bytes/sec
total size is 0 speedup is 0.00
rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1338) [sender=3.2.7]
root@srv1100186:~#
root@srv1100186:~# ls -la /docker/openclaw-zs4q/projects/repos/youtube-outreach-crm | head
total 8
drwxr-xr-x 2 ubuntu ubuntu 4096 Feb 15 08:03 .
drwxr-xr-x 3 root root 4096 Feb 15 08:03 ..
root@srv1100186:~#
That output is still showing a VPS shell prompt (root@srv1100186:~#) β so youβre still running rsync on the VPS. If you run it there, it canβt see your Macβs Desktop folder, and youβll get exactly what youβre seeing: total size is 0 + exit code 23.
Do this:
1) On your Mac (NOT in the SSH session)
Open a new Terminal tab/window where your prompt looks like:
dylandevrieze@MacBook⦠%
Check the folder exists:
ls -la "$HOME/Desktop/youtube-outreach-crm"
2) Run the upload from the Mac
(Use your VPS IP or, better, the Tailscale MagicDNS name)
Via public IP:
rsync -av --progress --delete \
--exclude node_modules --exclude .git --exclude dist --exclude build --exclude .next \
"$HOME/Desktop/youtube-outreach-crm/" \
root@76.13.143.94:/docker/openclaw-zs4q/projects/repos/youtube-outreach-crm/
Via Tailscale (preferred, avoids public SSH weirdness):
rsync -av --progress --delete \
--exclude node_modules --exclude .git --exclude dist --exclude build --exclude .next \
"$HOME/Desktop/youtube-outreach-crm/" \
root@srv1100186.tail777ba9.ts.net:/docker/openclaw-zs4q/projects/repos/youtube-outreach-crm/
3) Verify on the VPS
Back on the VPS:
ls -la /docker/openclaw-zs4q/projects/repos/youtube-outreach-crm | head
If you still get βcode 23β
Copy/paste the 10β20 lines above the βsent 23 bytesβ¦β summary (thatβs where the real error is), or run on the Mac:
rsync -avv --progress "$HOME/Desktop/youtube-outreach-crm/" root@76.13.143.94:/docker/openclaw-zs4q/projects/repos/youtube-outreach-crm/
and paste the first error lines.
Youβre still running the command on the VPS (your prompt is root@srv1100186:~#). Thatβs why the destination folder stays empty β the VPS canβt see your Macβs Desktop.
What to do
- Exit the VPS SSH session so youβre back on your Mac terminal:
exit
(Keep exiting until your prompt is something like dylandevrieze@MacBook⦠%.)
- On your Mac, confirm the folder actually has files:
ls -la "$HOME/Desktop/youtube-outreach-crm" | head
- Still on your Mac, upload to the VPS (recommended via Tailscale):
rsync -av --progress --delete \
--exclude node_modules --exclude .git --exclude dist --exclude build --exclude .next \
"$HOME/Desktop/youtube-outreach-crm/" \
root@srv1100186.tail777ba9.ts.net:/docker/openclaw-zs4q/projects/repos/youtube-outreach-crm/
- Back on the VPS, verify it arrived:
ls -la /docker/openclaw-zs4q/projects/repos/youtube-outreach-crm | head
If step (2) shows the folder is empty or βNo such fileβ, tell me the exact Desktop folder name (copy/paste from Finder path) and Iβll give you the exact rsync command.
I am on Windows now, how to check
On Windows, first make sure youβre running the upload from your Windows PC (not inside an SSH session on the VPS).
1) Check youβre local (Windows) vs on the VPS
If your prompt looks like root@srv1100186:~# youβre on the VPS.
On Windows (PowerShell), run:
hostname
whoami
You should see your Windows machine name + your Windows user (not srv1100186 / root).
2) Check the folder exists on your Desktop
PowerShell:
dir "$env:USERPROFILE\Desktop\youtube-outreach-crm"
If it lists files, youβre good.
3) Upload to the VPS (PowerShell, using built-in scp)
First check scp exists:
scp -V
Then upload (this copies the whole folder):
scp -r "$env:USERPROFILE\Desktop\youtube-outreach-crm" `
root@srv1100186.tail777ba9.ts.net:/docker/openclaw-zs4q/projects/repos/
That will create:
/docker/openclaw-zs4q/projects/repos/youtube-outreach-crm/ on the VPS.
4) Verify on the VPS + fix permissions
On the VPS:
ls -la /docker/openclaw-zs4q/projects/repos/youtube-outreach-crm | head
sudo chown -R 1000:1000 /docker/openclaw-zs4q/projects/repos/youtube-outreach-crm
If scp asks about a host key on Windows, thatβs normal on a new machineβjust make sure the fingerprint matches what you verified on the VPS earlier.
If you tell me whether youβre using PowerShell or WSL, I can give you the exact best command for your setup.
I am on mac an yes the folder exists on my desktop
Cool β then do the upload from your Mac terminal (not inside the VPS SSH session).
1) Make sure youβre on your Mac (local prompt)
Your prompt should look like dylandevrieze@MacBook⦠% (not root@srv...#).
Check the folder:
ls -la "$HOME/Desktop/youtube-outreach-crm" | head
2) Upload with rsync (recommended)
Use the Tailscale name (best):
rsync -av --progress --delete \
--exclude node_modules --exclude .git --exclude dist --exclude build --exclude .next \
"$HOME/Desktop/youtube-outreach-crm/" \
root@srv1100186.tail777ba9.ts.net:/docker/openclaw-zs4q/projects/repos/youtube-outreach-crm/
3) Verify on the VPS
ls -la /docker/openclaw-zs4q/projects/repos/youtube-outreach-crm | head
sudo chown -R 1000:1000 /docker/openclaw-zs4q/projects/repos/youtube-outreach-crm
If you still see total size is 0, thatβs the giveaway youβre not running it locally. In that case, paste the exact prompt line you see right before you run rsync (just the prompt, no secrets).
root@srv1100186:~# ls -la /docker/openclaw-zs4q/projects/repos/youtube-outreach-crm | head
sudo chown -R 1000:1000 /docker/openclaw-zs4q/projects/repos/youtube-outreach-crm
total 660
drwxr-xr-x 7 501 staff 4096 Jan 30 10:11 .
drwxr-xr-x 3 root root 4096 Feb 15 08:03 ..
-rw-r--r-- 1 501 staff 567 Jan 30 10:11 .env
-rw-r--r-- 1 501 staff 503 Jan 24 05:23 .gitignore
drwxr-xr-x 9 ubuntu ubuntu 4096 Feb 15 08:29 .next
-rw-r--r-- 1 501 staff 1683 Jan 24 05:35 README.md
-rw-r--r-- 1 501 staff 465 Jan 24 05:21 eslint.config.mjs
-rw-r--r-- 1 501 staff 251 Jan 30 10:09 next-env.d.ts
-rw-r--r-- 1 501 staff 133 Jan 24 05:21 next.config.ts
root@srv1100186:~#
Nice β the upload did land on the VPS (the folder isnβt empty anymore).
Right now the files are owned by your macOS UID (501 staff), and most files are -rw-r--r--, so OpenClaw (and the VPS user) wonβt be able to edit them. Fix ownership/permissions on the VPS:
1) Run this on the VPS (as root)
sudo chown -R 1000:1000 /docker/openclaw-zs4q/projects/repos/youtube-outreach-crm
sudo chmod -R u+rwX /docker/openclaw-zs4q/projects/repos/youtube-outreach-crm
2) Verify
ls -la /docker/openclaw-zs4q/projects/repos/youtube-outreach-crm | head
You should no longer see 501 staff.
3) Then tell OpenClaw where to work
In the Control UI prompt:
βWork in
/projects/repos/youtube-outreach-crm/.β
Two quick notes:
- You uploaded
.envβ donβt paste its contents in Discord (likely contains secrets). - You uploaded
.next/(build output). Next time, exclude it when syncing; itβs usually better to rebuild on the server.
If you want, tell me whether you want to run this Next.js app on the VPS (tailnet link like we did for the static site), and Iβll give you the clean βadd a docker-compose service + tailscale serveβ steps.
root@srv1100186:~# ls -la /docker/openclaw-zs4q/projects/repos/youtube-outreach-crm | head
total 660
drwxr-xr-x 7 ubuntu ubuntu 4096 Jan 30 10:11 .
drwxr-xr-x 3 root root 4096 Feb 15 08:03 ..
-rw-r--r-- 1 ubuntu ubuntu 567 Jan 30 10:11 .env
-rw-r--r-- 1 ubuntu ubuntu 503 Jan 24 05:23 .gitignore
drwxr-xr-x 9 ubuntu ubuntu 4096 Feb 15 08:29 .next
-rw-r--r-- 1 ubuntu ubuntu 1683 Jan 24 05:35 README.md
-rw-r--r-- 1 ubuntu ubuntu 465 Jan 24 05:21 eslint.config.mjs
-rw-r--r-- 1 ubuntu ubuntu 251 Jan 30 10:09 next-env.d.ts
-rw-r--r-- 1 ubuntu ubuntu 133 Jan 24 05:21 next.config.ts
root@srv1100186:~#
Nice β ownership is now ubuntu:ubuntu, which is exactly what we want as long as ubuntu is UID 1000 (it usually is).
0) Quick checks (VPS)
id -u ubuntu; id -g ubuntu
If that prints 1000 / 1000, youβre good.
Also, since .env likely has secrets, lock it down:
chmod 600 /docker/openclaw-zs4q/projects/repos/youtube-outreach-crm/.env
Make the Next.js app accessible on a Tailnet link
1) Add a new service to docker-compose.yml
Edit:
nano /docker/openclaw-zs4q/docker-compose.yml
Add this new service under services: (next to openclaw: / site1:):
youtube-crm:
image: node:20-alpine
working_dir: /app
restart: unless-stopped
volumes:
- ./projects/repos/youtube-outreach-crm:/app
env_file:
- ./projects/repos/youtube-outreach-crm/.env
ports:
- "127.0.0.1:3001:3000"
command: sh -lc "npm ci || npm install && npm run build && npm run start -- -p 3000"
Bring it up:
docker compose up -d youtube-crm
docker logs -f openclaw-zs4q-youtube-crm-1
Test locally on the VPS:
curl -I http://127.0.0.1:3001
2) Expose it via Tailscale Serve (tailnet-only)
Use another HTTPS port (8444) so it doesnβt collide with your other serves:
sudo tailscale serve --bg --https=8444 http://127.0.0.1:3001
tailscale serve status
Then open (from a Tailnet device):
https://srv1100186.tail777ba9.ts.net:8444/
To turn it off later:
sudo tailscale serve --https=8444 off
If youtube-outreach-crm uses pnpm or yarn (no package-lock.json), tell me which lockfile you have (package-lock.json / pnpm-lock.yaml / yarn.lock) and Iβll adjust the command: line so installs/builds work reliably.
services:
openclaw:
image: ghcr.io/hostinger/hvps-openclaw:latest
init: true
ports:
- "127.0.0.1:${PORT}:${PORT}"
env_file:
- .env
restart: unless-stopped
volumes:
- ./data:/data
- ./data/linuxbrew:/home/linuxbrew
- ./projects:/projects
site1:
image: nginx:alpine
restart: unless-stopped
volumes:
- ./projects/sites/site1:/usr/share/nginx/html:ro
ports:
- "127.0.0.1:3000:80"
Yep β your docker-compose.yml looks good so far. To run your Next.js app on the VPS + give it a Tailnet link, add one more service under services: (at the same indentation level as site1:).
1) Add this to the bottom of your compose file
youtube-crm:
image: node:20-alpine
working_dir: /app
restart: unless-stopped
volumes:
- ./projects/repos/youtube-outreach-crm:/app
env_file:
- ./projects/repos/youtube-outreach-crm/.env
ports:
- "127.0.0.1:3001:3000"
command: sh -lc "npm ci || npm install && npm run build && npm run start -- -p 3000 -H 0.0.0.0"
2) Apply + check it runs
On the VPS (in /docker/openclaw-zs4q):
docker compose config
docker compose up -d youtube-crm
docker logs -f openclaw-zs4q-youtube-crm-1
Test locally:
curl -I http://127.0.0.1:3001
3) Expose it tailnet-only via Tailscale
Use a new HTTPS port (8444) so it doesnβt collide with your other services:
sudo tailscale serve --bg --https=8444 http://127.0.0.1:3001
tailscale serve status
Then open:
https://srv1100186.tail777ba9.ts.net:8444/
One quick question: does your project use npm (package-lock.json), pnpm (pnpm-lock.yaml), or yarn (yarn.lock)? If itβs pnpm/yarn, we should tweak the command: so installs donβt fail.
root@srv1100186:~# docker compose config
docker compose up -d youtube-crm
docker logs -f openclaw-zs4q-youtube-crm-1
no configuration file provided: not found
no configuration file provided: not found
Error response from daemon: No such container: openclaw-zs4q-youtube-crm-1
root@srv1100186:~#
Those errors mean you ran docker compose from a directory that doesnβt contain a docker-compose.yml (youβre at root@srv1100186:~#).
Run compose from the right folder (on the VPS)
cd /docker/openclaw-zs4q
docker compose config
If that works, then start the service:
docker compose up -d youtube-crm
And follow logs the easy way (no guessing container name):
docker compose logs -f youtube-crm
If it says βno such service: youtube-crmβ
That means you havenβt saved the youtube-crm: block into /docker/openclaw-zs4q/docker-compose.yml yet (or indentation is off). Paste your full services: section again after you add it and Iβll spot the YAML issue quickly.
Quick sanity check
After up -d:
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
root@srv1100186:/docker/openclaw-zs4q# docker compose up -d youtube-crm
WARN[0000] /docker/openclaw-zs4q/docker-compose.yml: the attribute version is obsolete, it will be ignored, please remove it to avoid potential confusion
[+] up 7/7
β Image node:20-alpine Pulled 7.9s
β Container openclaw-zs4q-youtube-crm-1 Created 0.2s
root@srv1100186:/docker/openclaw-zs4q# docker compose logs -f youtube-crm
WARN[0000] /docker/openclaw-zs4q/docker-compose.yml: the attribute version is obsolete, it will be ignored, please remove it to avoid potential confusion
youtube-crm-1 | npm warn deprecated node-domexception@1.0.0: Use your platform's native DOMException instead
Nice β itβs running and installing deps/building.
A couple quick notes:
1) That version is obsolete warning
Safe to ignore. If you want to clean it up later, just remove the top version: "..." line from docker-compose.yml (optional).
2) Follow the logs until it says itβs listening
Keep watching docker compose logs -f youtube-crm until you see something like:
ready - started server on 0.0.0.0:3000(Next.js)
or similar.
Then test locally on the VPS:
curl -I http://127.0.0.1:3001
3) Expose it over Tailnet once local works
sudo tailscale serve --bg --https=8444 http://127.0.0.1:3001
tailscale serve status
Open (from a Tailnet device):
https://srv1100186.tail777ba9.ts.net:8444/
If the container exits or keeps restarting, paste the last ~50 log lines and weβll fix the start command (sometimes it needs npm run start -- -p 3000 -H 0.0.0.0 vs next start).
youtube-crm-1 | To address all issues (including breaking changes), run:
youtube-crm-1 | npm audit fix --force
youtube-crm-1 |
youtube-crm-1 | Run npm audit for details.
youtube-crm-1 |
youtube-crm-1 | > youtube-outreach-crm@0.1.0 build
youtube-crm-1 | > next build
youtube-crm-1 |
youtube-crm-1 | β² Next.js 16.1.4 (Turbopack)
youtube-crm-1 | - Environments: .env
youtube-crm-1 |
youtube-crm-1 | β The "middleware" file convention is deprecated. Please use "proxy" instead. Learn more: https://nextjs.org/docs/messages/middleware-to-proxy
youtube-crm-1 | Creating an optimized production build ...
youtube-crm-1 | β Compiled successfully in 18.2s
youtube-crm-1 | Running TypeScript ...
maybe it's better to start from scratch?
No need to start from scratch β what youβre seeing is pretty normal:
npm audit fix --forceis just npm suggesting an (often risky) dependency upgrade path. Donβt run that automatically unless youβre ready to deal with breaking changes.- The βmiddleware convention is deprecatedβ warning is a Next.js warning, not a failure.
- It successfully compiled, and now itβs doing TypeScript checks (βRunning TypeScript ...β).
What to do next
-
Wait for the build to finish. If itβs going to fail, itβll print TypeScript errors next.
-
In another VPS terminal, check whether the container is still running:
cd /docker/openclaw-zs4q
docker compose ps
- Once logs show it started the server (something like βstarted server on 0.0.0.0:3000β), test:
curl -I http://127.0.0.1:3001
- Then you can expose it tailnet-only:
sudo tailscale serve --bg --https=8444 http://127.0.0.1:3001
If it hangs on βRunning TypeScript ...β
Paste the next ~30 lines of logs (or if it exits, the last ~80 lines). Usually itβs either:
- a TypeScript error, or
- it finished and moved on to
npm run startbut you didnβt scroll far enough yet.
youtube-crm-1 | 5 vulnerabilities (4 low, 1 high)
youtube-crm-1 |
youtube-crm-1 | To address issues that do not require attention, run:
youtube-crm-1 | npm audit fix
youtube-crm-1 |
youtube-crm-1 | To address all issues (including breaking changes), run:
youtube-crm-1 | npm audit fix --force
youtube-crm-1 |
youtube-crm-1 | Run npm audit for details.
youtube-crm-1 |
youtube-crm-1 | > youtube-outreach-crm@0.1.0 build
youtube-crm-1 | > next build
youtube-crm-1 |
youtube-crm-1 | β² Next.js 16.1.4 (Turbopack)
youtube-crm-1 | - Environments: .env
youtube-crm-1 |
youtube-crm-1 | β The "middleware" file convention is deprecated. Please use "proxy" instead. Learn more: https://nextjs.org/docs/messages/middleware-to-proxy
youtube-crm-1 | Creating an optimized production build ...
youtube-crm-1 | β Compiled successfully in 17.5s
youtube-crm-1 | Running TypeScript ...
Donβt start from scratch β this is just Next.js doing type-checking as part of next build. On some projects it can take a few minutes, and it doesnβt print anything while itβs running.
1) Check whether itβs actually still working (new VPS terminal)
Run:
cd /docker/openclaw-zs4q
docker compose ps
docker stats --no-stream openclaw-zs4q-youtube-crm-1
- If CPU is nonβzero / memory is changing β itβs still running typecheck/build.
- If the container is βRestartingβ β itβs crashing; we need the error.
2) If itβs restarting/crashing, grab the last logs
docker compose logs --tail=200 youtube-crm
Paste the last ~50 lines.
3) If itβs βrunningβ but stuck at βRunning TypeScript β¦β for >5 minutes
Get a peek at what itβs doing:
docker exec -it openclaw-zs4q-youtube-crm-1 sh -lc "ps aux | head -40"
(or ps aux | grep -E 'next|tsc|node')
About the βnpm auditβ vulnerabilities line
Ignore for now (especially for a private tailnet preview). Donβt run npm audit fix --force unless youβre ready to deal with breaking dependency changes.
Send me the output of docker compose ps + docker stats --no-stream ... and Iβll tell you whether itβs just slow or actually stuck/crashing.
I want to remove it
Sure β what exactly do you want to remove?
Here are the common βremoveβ targets, with the commands for each.
A) Remove the Tailnet link (stop serving it)
If you enabled it on 8444:
sudo tailscale serve --https=8444 off
tailscale serve status
B) Stop + remove the youtube-crm container (but keep files)
cd /docker/openclaw-zs4q
docker compose stop youtube-crm
docker compose rm -f youtube-crm
docker compose ps
C) Remove it from docker-compose (so it wonβt come back)
Edit:
nano /docker/openclaw-zs4q/docker-compose.yml
Delete the whole youtube-crm: service block, then:
docker compose up -d
D) Delete the uploaded project files too (irreversible)
Iβd rather archive than delete:
mkdir -p /docker/openclaw-zs4q/projects/_archived
mv /docker/openclaw-zs4q/projects/repos/youtube-outreach-crm \
/docker/openclaw-zs4q/projects/_archived/youtube-outreach-crm-$(date +%F)
If you truly want to delete:
rm -rf /docker/openclaw-zs4q/projects/repos/youtube-outreach-crm
Reply with just A, B, C, D, or a combo (e.g. βA+B+Cβ), and Iβll tell you the safest order for your case.
1 updates could not be installed automatically. For more details,
see /var/log/unattended-upgrades/unattended-upgrades.log
Last login: Sun Feb 15 11:08:03 2026 from 169.254.0.1
root@srv1100186:~# cd /projects/postiz
docker compose up -d
-bash: cd: /projects/postiz: No such file or directory
no configuration file provided: not found
root@srv1100186:~# sudo tailscale serve --https=8444 off
tailscale serve status
error: failed to remove web serve: handler does not exist
try tailscale serve --help for usage info
https://srv1100186.tail777ba9.ts.net (tailnet only)
|-- / proxy http://127.0.0.1:43710
https://srv1100186.tail777ba9.ts.net:8443 (tailnet only)
|-- / proxy http://127.0.0.1:3000
|-- /crm proxy http://127.0.0.1:3001
?
You said βremove itβ, but Iβm not sure which βitβ you mean. If you mean remove the youtube-crm Next.js thing we just added (stop it running + remove the Tailnet link), do this on the VPS:
1) Turn off the Tailnet link (8444)
sudo tailscale serve --https=8444 off
tailscale serve status
2) Stop + remove the container
cd /docker/openclaw-zs4q
docker compose stop youtube-crm
docker compose rm -f youtube-crm
3) (Recommended) Remove it from docker-compose.yml so it wonβt come back
Edit:
nano /docker/openclaw-zs4q/docker-compose.yml
Delete the whole youtube-crm: service block, then run:
docker compose up -d
If instead you meant βdelete the uploaded project files tooβ, tell me and Iβll give you the safe archive/delete commands.
root@srv1100186:~# cd /docker/openclaw-zs4q
docker compose stop youtube-crm
docker compose rm -f youtube-crm
WARN[0000] /docker/openclaw-zs4q/docker-compose.yml: the attribute version is obsolete, it will be ignored, please remove it to avoid potential confusion
[+] stop 1/1
β Container openclaw-zs4q-youtube-crm-1 Stopped 0.0s
WARN[0000] /docker/openclaw-zs4q/docker-compose.yml: the attribute version is obsolete, it will be ignored, please remove it to avoid potential confusion
Going to remove openclaw-zs4q-youtube-crm-1
[+] remove 1/1
β Container openclaw-zs4q-youtube-crm-1 Removed 1.3s
root@srv1100186:/docker/openclaw-zs4q# nano /docker/openclaw-zs4q/docker-compose.yml
root@srv1100186:/docker/openclaw-zs4q# docker compose up -d
WARN[0000] /docker/openclaw-zs4q/docker-compose.yml: the attribute version is obsolete, it will be ignored, please remove it to avoid potential confusion
[+] up 2/2
β Container openclaw-zs4q-site1-1 Running 0.0s
β Container openclaw-zs4q-openclaw-1 Running 0.0s
root@srv1100186:/docker/openclaw-zs4q#
Perfect β youtube-crm is fully removed (container stopped + deleted + compose updated). Only openclaw and site1 are running now.
Two optional cleanups:
1) Remove the Tailnet link if you created one for it (8444)
Check:
tailscale serve status
If you still see :8444, turn it off:
sudo tailscale serve --https=8444 off
2) Remove the obsolete version: line warning
Open your compose file and delete the top version: "..." line (safe), then:
docker compose up -d
If you want to also delete/archive the uploaded repo files (/docker/openclaw-zs4q/projects/repos/youtube-outreach-crm), say so and Iβll give the safest (archive-first) command.
https://srv1100186.tail777ba9.ts.net:8443 (tailnet only)
|-- / proxy http://127.0.0.1:3000
|-- /crm proxy http://127.0.0.1:3001
root@srv1100186:/docker/openclaw-zs4q#
all good?
Easiest safe way: briefly disable the whole 8443 serve, then re-enable only the root mapping you want (this removes the /crm path). Run these on the VPS:
- Turn off the current 8443 serve (removes both / and /crm on that port):
sudo tailscale serve --https=8443 off
- Re-add only the root proxy to 127.0.0.1:3000:
sudo tailscale serve --bg --https=8443 http://127.0.0.1:3000
- Confirm status:
tailscale serve status
You should then see only:
https://srv1100186.tail777ba9.ts.net:8443/ |-- / proxy http://127.0.0.1:3000
Notes:
- This wonβt touch your OpenClaw UI serve on the other port (thatβs separate).
- If youβd prefer a non-disruptive backup-first approach, run:
sudo tailscale serve status --json > /tmp/serve-backup.json
sudo tailscale serve reset
# then re-add the root mapping:
sudo tailscale serve --bg --https=8443 http://127.0.0.1:3000
(Use the backup if you need to restore the full previous config.)
Which way do you want to go β quick (off + re-add) or backup+reset?
Yep β if after running those commands tailscale serve status shows only the / -> 127.0.0.1:3000 mapping (and no /crm), youβre good.
Quick check:
tailscale serve status
curl -I http://127.0.0.1:3000
And open: https://srv1100186.tail777ba9.ts.net:8443/ from your Tailnet device.
This site canβt be reached
srv1100186.tail777ba9.ts.net refused to connect.
Try:
Checking the connection
Checking the proxy and the firewall
ERR_CONNECTION_REFUSED
ERR_CONNECTION_REFUSED usually means Tailscale Serve is up, but nothing is listening behind it (or you opened the wrong URL/port).
0) Make sure youβre opening the site URL (itβs on 8443)
Your website is:
If you open without :8443, youβre hitting port 443 (which in your setup is OpenClaw, not the site).
1) On the VPS: confirm what Tailscale is serving
tailscale serve status
You should see something like:
https://...:8443 |-- / proxy http://127.0.0.1:3000
2) On the VPS: check the backend (nginx) is actually up
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
curl -I http://127.0.0.1:3000
- If the
curlfails: restart the site container:
cd /docker/openclaw-zs4q
docker compose up -d site1
3) If 8443 mapping is missing/broken, re-add it
sudo tailscale serve --https=8443 off
sudo tailscale serve --bg --https=8443 http://127.0.0.1:3000
tailscale serve status
If you paste the outputs of tailscale serve status + curl -I http://127.0.0.1:3000, I can tell you exactly which piece is down.
Okay that works, now I am wondering if I want openlclaw to create projects is there a way it can make it visible on my local pc through tailnet without needing to do all thos manual steps through the terminal every time?
Yes β you can set it up so you do the βterminal stuffβ once, and after that anything OpenClaw writes into /projects/sites/... is automatically visible from your PC via the Tailnet URL.
Best pattern: one βsites preview serverβ (static sites)
Instead of a separate container per site, run one container that serves the whole folder:
/projects/sites/<site-name>/index.html
1) Change your site1 service into a generic βsitesβ server
Edit:
nano /docker/openclaw-zs4q/docker-compose.yml
Replace your current site1: service with this:
sites:
image: caddy:2-alpine
restart: unless-stopped
volumes:
- ./projects/sites:/srv:ro
ports:
- "127.0.0.1:3000:80"
command: ["caddy","file-server","--root","/srv","--browse","--listen",":80"]
Apply:
cd /docker/openclaw-zs4q
docker compose up -d
Now your site URLs become:
http://127.0.0.1:3000/site1/http://127.0.0.1:3000/site2/
etc.
2) Keep Tailscale Serve the same (one-time)
Point Tailnet HTTPS to that one port (you already do this):
sudo tailscale serve --bg --https=8443 http://127.0.0.1:3000
Now from your local PC (on Tailnet):
https://srv1100186.tail777ba9.ts.net:8443/site1/https://srv1100186.tail777ba9.ts.net:8443/site2/
3) Prompt OpenClaw to always write to the right place
Tell it:
βCreate the site in
/projects/sites/<new-site-name>/(this is served automatically).β
Thatβs it β no more terminal steps per new static site.
What about βmore complex projectsβ (Next.js, APIs, DBs)?
Those need a running server process, so there will still be some βstart/stopβ step (or you build a proper deploy system). But for static sites, the setup above is the smoothest.
If you want, paste your current site1 block after you edit, and Iβll sanity-check it before you restart anything.
so how can I get it to do complex things like coding, setting up postiz on its onw?
It can help with βcomplex thingsβ, but the safe/realistic way to do it is:
- OpenClaw writes code + config files (docker-compose, Caddy/Nginx config, README with commands)
- You run the actual commands (
docker compose up -d, etc.) after you review
(If you later want more automation, you can, but start human-in-the-loop.)
Pattern that works well (for Postiz or any app)
- Create a dedicated folder (on VPS host):
mkdir -p /docker/openclaw-zs4q/projects/repos/postiz
Inside the container that becomes: /projects/repos/postiz/
- In OpenClaw UI, use a prompt like:
βWork in
/projects/repos/postiz/.
Goal: deploy Postiz via Docker Compose on this VPS.
Requirements: tailnet-only access (no public ports), store secrets in.env(donβt print them), and write aREADME.mdwith exact commands I should run.
Ask me before any destructive step.β
- You paste Postizβs official install/deploy instructions into the chat (or link them), and OpenClaw turns that into:
docker-compose.yml.env.example(placeholders)README.md(runbook)
- You run the commands from the right directory:
cd /docker/openclaw-zs4q/projects/repos/postiz
docker compose up -d
- Expose it tailnet-only (one-time):
- bind the app to localhost (
127.0.0.1:<port>) - then
tailscale serve --bg --https=<port> http://127.0.0.1:<port>
If you truly want βon its ownβ
That means letting OpenClaw run shell commands (install packages, start containers). You can do that, but you should treat it like giving an intern root on your server: doable, but risky. Iβd only do it with:
- an allowlist / approvals for commands
- a separate βprojectsβ area only
- backups/snapshots enabled
If you tell me where Postizβs docs are (or paste the docker-compose they recommend) and whether you want it tailnet-only or public, Iβll tell you the cleanest deployment shape for your current setup (Docker + Tailscale Serve).
openclaw stopped working
openclaw-1:
(Use node --trace-deprecation ... to show where the warning was created)
[Mon Feb 16 2026 08:50:10.182]
openclaw-1:
2026-02-16T01:50:10.182Z [ws] Proxy headers detected from untrusted address. Connection will not be treated as local. Configure gateway.trustedProxies to restore local client detection behind your proxy.
[Mon Feb 16 2026 08:50:10.491]
openclaw-1:
2026-02-16T01:50:10.491Z [ws] webchat connected conn=cdb7c1f6-1082-41bf-bf65-5918b0cf141f remote=127.0.0.1 client=openclaw-control-ui webchat vdev
Error: EACCES: permission denied, mkdir '/projects'
You can get pretty close to βno terminal fiddlingβ, but there are two different cases:
1) βProjectsβ that are just files (static sites) β can be 100% automatic
If you set up one file server that serves all of /projects/sites, then any time OpenClaw creates:
/projects/sites/<new-site>/index.html
β¦itβs instantly visible on your PC at a stable Tailnet URL (no new containers, no new tailscale serve commands).
If you want, we can change your current site1 nginx service into a single βsitesβ server so you get URLs like:
https://srv1100186.tail777ba9.ts.net:8443/site1/
https://srv1100186.tail777ba9.ts.net:8443/site2/ etc.
2) βComplex thingsβ (Postiz, Next.js apps, databases) β you still need a deploy/start step
Anything dynamic needs a running process (containers/services). The good news: you only do the start step once per app, then it stays up and the link stays stable.
The low-friction workflow I recommend
- OpenClaw generates a complete stack folder:
/projects/stacks/postiz/docker-compose.yml/projects/stacks/postiz/.env.example/projects/stacks/postiz/README.md(exact commands)
- You run one command on the VPS:
cd /docker/openclaw-zs4q/projects/stacks/postiz
docker compose up -d
- You expose it tailnet-only once (pick a port, e.g. 8445):
sudo tailscale serve --bg --https=8445 http://127.0.0.1:<postiz-local-port>
After that, you donβt keep redoing terminal steps unless you redeploy.
Can OpenClaw do the deploy commands βon its ownβ?
Only if you intentionally give it a safe way to run host commands (tight allowlist, approvals). Thatβs doable but itβs a security decision, not a default.
If you tell me which direction you want:
- Make static sites fully automatic (one βsitesβ server + one Tailnet URL), or
- Set up Postiz tailnet-only with the βone command deployβ runbook,
β¦Iβll give you the exact steps for that path.
Got it. That EACCES: permission denied, mkdir '/projects' means the Gateway canβt write to your /projects mount (workspace/projects volume), and it can break a bunch of UI actions.
Run these on the VPS and paste the output:
cd /docker/openclaw-zs4q
docker compose ps
docker compose logs --tail=120 openclaw
docker exec -it openclaw-zs4q-openclaw-1 sh -lc '
echo "workspace=$(openclaw config get agents.defaults.workspace)";
ps aux | head -25;
ls -ldn /projects /projects/_openclaw-workspace || true;
touch /projects/_perm_test && rm /projects/_perm_test && echo "touch OK" || echo "touch FAILED"
'
If touch FAILED, fix perms on the host (safe)
sudo chown -R 1000:1000 /docker/openclaw-zs4q/projects
sudo chmod -R u+rwX /docker/openclaw-zs4q/projects
docker compose restart openclaw
About the proxy warning
This line is harmless:
βProxy headers detected from untrusted addressβ¦ configure gateway.trustedProxiesβ¦β
It just affects whether OpenClaw treats proxied requests as βlocalβ (auto-approve behavior). We can clean it up later; it shouldnβt stop things from working.
Send the outputs above and Iβll tell you exactly whatβs wrong (wrong mount, wrong owner, or OpenClaw running as a different UID than we expected).
1 updates could not be installed automatically. For more details,
see /var/log/unattended-upgrades/unattended-upgrades.log
Last login: Mon Feb 16 00:57:43 2026 from 169.254.0.1
root@srv1100186:~# sudo tailscale serve --https=8443 off
Are you sure you want to delete 2 handlers under port 8443? [Y/n] y
root@srv1100186:~# sudo tailscale serve --bg --https=8443 http://127.0.0.1:3000
Available within your tailnet:
https://srv1100186.tail777ba9.ts.net:8443/
|-- proxy http://127.0.0.1:3000
Serve started and running in the background.
To disable the proxy, run: tailscale serve --https=8443 off
root@srv1100186:~# tailscale serve status
https://srv1100186.tail777ba9.ts.net (tailnet only)
|-- / proxy http://127.0.0.1:43710
https://srv1100186.tail777ba9.ts.net:8443 (tailnet only)
|-- / proxy http://127.0.0.1:3000
root@srv1100186:~# cd /docker/openclaw-zs4q/projects/stacks/postiz
docker compose up -d
-bash: cd: /docker/openclaw-zs4q/projects/stacks/postiz: No such file or directory
no configuration file provided: not found
root@srv1100186:~# openclaw dashboard
openclaw: command not found
root@srv1100186:~#
how can I fix openclaw as it has stopped responding
A few things are getting mixed together here:
openclawis not installed on the VPS host, itβs inside the Docker container β thatβs whyopenclaw: command not found.docker compose up -donly works when you run it in the folder that has your compose file:/docker/openclaw-zs4q./docker/openclaw-zs4q/projects/stacks/postizdoesnβt exist yet because you havenβt created/deployed Postiz (so that error is expected).
Letβs get OpenClaw responding again.
1) Check the OpenClaw container is running + restart it
On the VPS:
cd /docker/openclaw-zs4q
docker compose ps
docker compose logs --tail=200 openclaw
docker compose restart openclaw
2) Verify the Gateway port is reachable locally
(Your tailnet root is proxying to 127.0.0.1:43710, so test that first)
curl -I http://127.0.0.1:43710
If that fails, paste the output of docker compose ps + the last ~50 lines of docker compose logs --tail=200 openclaw.
3) Verify /projects is writable inside the container (fixes EACCES)
Run:
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'ls -ldn /projects /projects/_openclaw-workspace; touch /projects/_perm_test && rm /projects/_perm_test && echo OK || echo FAIL'
If it prints FAIL, fix on the host:
sudo chown -R 1000:1000 /docker/openclaw-zs4q/projects
sudo chmod -R u+rwX /docker/openclaw-zs4q/projects
docker compose restart openclaw
4) If the UI connects but βstops respondingβ
Often itβs actually βdevice pairing required againβ after a restart. Approve from the VPS:
docker exec -it openclaw-zs4q-openclaw-1 openclaw devices list
docker exec -it openclaw-zs4q-openclaw-1 openclaw devices approve <requestId>
Quick note on your Tailscale Serve
Your Serve status looks fine now:
- OpenClaw UI:
https://srv1100186...ts.net/β127.0.0.1:43710
- Website:
https://srv1100186...ts.net:8443/β127.0.0.1:3000
Run steps (1) + (2) and tell me what curl -I http://127.0.0.1:43710 returns (200? 401? connection refused?). That will pinpoint what βstopped respondingβ actually is.
openclaw-1 | 2026-02-16T01:46:17.731Z [ws] Proxy headers detected from untrusted address. Connection will not be treated as local. Configure gateway.trustedProxies to restore local client detection behind your proxy.
openclaw-1 | 2026-02-16T01:46:17.956Z [ws] webchat connected conn=105f8178-77e6-4161-9bed-0db33470ee45 remote=127.0.0.1 client=openclaw-control-ui webchat vdev
openclaw-1 | 2026-02-16T01:49:19.566Z [telegram] callback handler failed: Error: EACCES: permission denied, mkdir '/projects'
openclaw-1 | 2026-02-16T01:49:23.276Z [telegram] handler failed: Error: EACCES: permission denied, mkdir '/projects'
openclaw-1 | 2026-02-16T01:50:01.578Z [ws] webchat disconnected code=1005 reason=n/a conn=105f8178-77e6-4161-9bed-0db33470ee45
openclaw-1 | (node:9) [DEP0060] DeprecationWarning: The util._extend API is deprecated. Please use Object.assign() instead.
openclaw-1 | (Use node --trace-deprecation ... to show where the warning was created)
openclaw-1 | 2026-02-16T01:50:10.182Z [ws] Proxy headers detected from untrusted address. Connection will not be treated as local. Configure gateway.trustedProxies to restore local client detection behind your proxy.
openclaw-1 | 2026-02-16T01:50:10.491Z [ws] webchat connected conn=cdb7c1f6-1082-41bf-bf65-5918b0cf141f remote=127.0.0.1 client=openclaw-control-ui webchat vdev
openclaw-1 | 2026-02-16T01:56:45.271Z [ws] Proxy headers detected from untrusted address. Connection will not be treated as local. Configure gateway.trustedProxies to restore local client detection behind your proxy.
openclaw-1 | 2026-02-16T01:56:45.614Z [ws] webchat connected conn=cf34a732-c5fb-4e62-a6e2-a16da3b49019 remote=127.0.0.1 client=openclaw-control-ui webchat vdev
[+] restart 0/1
β Container openclaw-zs4q-openclaw-1 Restarting
Your logs show the real problem:
EACCES: permission denied, mkdir '/projects'
So OpenClaw is trying to create/use /projects, but inside the container it either isnβt mounted or isnβt writable, and thatβs causing handlers to fail and the container to flap.
Do these checks/fixes on the VPS:
1) Verify /projects is actually mounted into the OpenClaw container (and RW)
cd /docker/openclaw-zs4q
docker inspect openclaw-zs4q-openclaw-1 --format '{{range .Mounts}}{{println .Destination "->" .Source "rw=" .RW}}{{end}}' | sort
You must see a line like:
/projects -> /docker/openclaw-zs4q/projects rw=true
- If you do NOT see
/projects: your compose file lost the- ./projects:/projectsmount. Add it back under theopenclaw:service and run:
docker compose up -d --force-recreate openclaw
2) Ensure the host folder exists + is writable
ls -ldn /docker/openclaw-zs4q/projects
ls -ldn /docker/openclaw-zs4q/projects/_openclaw-workspace || true
sudo mkdir -p /docker/openclaw-zs4q/projects/_openclaw-workspace
sudo chown -R 1000:1000 /docker/openclaw-zs4q/projects
sudo chmod -R u+rwX /docker/openclaw-zs4q/projects
3) Restart OpenClaw cleanly
cd /docker/openclaw-zs4q
docker compose up -d --force-recreate openclaw
docker compose logs -f openclaw
4) Quick write test inside the container
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'touch /projects/_perm_test && rm /projects/_perm_test && echo OK || echo FAIL'
If you paste the output of step (1) (the mounts list), I can tell you immediately whether this is βmissing mountβ vs βpermissionsβ.
root@srv1100186:~# cd /docker/openclaw-zs4q
docker inspect openclaw-zs4q-openclaw-1 --format '{{range .Mounts}}{{println .Destination "->" .Source "rw=" .RW}}{{end}}' | sort
/data -> /docker/openclaw-zs4q/data rw= true
/home/linuxbrew -> /docker/openclaw-zs4q/data/linuxbrew rw= true
root@srv1100186:/docker/openclaw-zs4q#
Thereβs the smoking gun: /projects is not mounted into the OpenClaw container anymore. Your container only has /data and /home/linuxbrew, so when OpenClaw tries to use /projects (your workspace), it hits EACCES / fails and starts flapping.
Fix: add the /projects volume back + recreate the container
- Make sure the host folder exists:
sudo mkdir -p /docker/openclaw-zs4q/projects
sudo chown -R 1000:1000 /docker/openclaw-zs4q/projects
- Edit your compose file:
nano /docker/openclaw-zs4q/docker-compose.yml
Under the openclaw service, make sure volumes: includes this line:
volumes:
- ./data:/data
- ./data/linuxbrew:/home/linuxbrew
- ./projects:/projects
- Recreate OpenClaw:
cd /docker/openclaw-zs4q
docker compose up -d --force-recreate openclaw
- Confirm the mount is back:
docker inspect openclaw-zs4q-openclaw-1 --format '{{range .Mounts}}{{println .Destination "->" .Source "rw=" .RW}}{{end}}' | sort
You should now see /projects -> /docker/openclaw-zs4q/projects rw= true.
- Quick write test:
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'mkdir -p /projects/_openclaw-workspace && touch /projects/_perm_test && rm /projects/_perm_test && echo OK'
Once /projects is mounted again, OpenClaw should stop βstopping workingβ immediately.
services:
openclaw:
image: ghcr.io/hostinger/hvps-openclaw:latest
init: true
ports:
- "127.0.0.1:${PORT}:${PORT}"
env_file:
- .env
restart: unless-stopped
volumes:
- ./data:/data
- ./data/linuxbrew:/home/linuxbrew
Yep β thatβs exactly why /projects disappeared. Just add the missing line back.
Change your volumes: block to:
volumes:
- ./data:/data
- ./data/linuxbrew:/home/linuxbrew
- ./projects:/projects
Then on the VPS:
cd /docker/openclaw-zs4q
docker compose up -d --force-recreate openclaw
Verify the mount exists now:
docker inspect openclaw-zs4q-openclaw-1 --format '{{range .Mounts}}{{println .Destination "->" .Source "rw=" .RW}}{{end}}' | sort
You should see /projects -> /docker/openclaw-zs4q/projects rw= true. After that, OpenClaw should respond again.
docker compose up -d --force-recreate openclaw
[+] up 1/1
β Container openclaw-zs4q-openclaw-1 Recreated 2.3s
root@srv1100186:/docker/openclaw-zs4q# docker inspect openclaw-zs4q-openclaw-1 --format '{{range .Mounts}}{{println .Destination "->" .Source "rw=" .RW}}{{end}}' | sort
/data -> /docker/openclaw-zs4q/data rw= true
/home/linuxbrew -> /docker/openclaw-zs4q/data/linuxbrew rw= true
/projects -> /docker/openclaw-zs4q/projects rw= true
root@srv1100186:/docker/openclaw-zs4q#
Perfect β /projects is mounted again. That should fix the EACCES mkdir /projects errors and OpenClaw should respond.
Final quick sanity check (optional):
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'touch /projects/_perm_test && rm /projects/_perm_test && echo OK'
docker compose logs --tail=50 openclaw
If the UI still wonβt connect, itβs usually just device pairing again after the recreate:
docker exec -it openclaw-zs4q-openclaw-1 openclaw devices list
works again how do I install this clawhub skill? https://clawhub.ai/theglove44/reddit
Yep. Since your OpenClaw is running in Docker and your workspace is /projects/_openclaw-workspace, you want to install the skill into:
/projects/_openclaw-workspace/skills/
1) Install via ClawHub CLI (run on the VPS)
This uses npx so you donβt have to βinstall clawhubβ permanently:
docker exec -it openclaw-zs4q-openclaw-1 sh -lc '
cd /projects/_openclaw-workspace
npx -y clawhub install theglove44/reddit --workdir /projects/_openclaw-workspace
'
If that slug doesnβt work, search for the exact slug first:
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'npx -y clawhub search "reddit" --limit 10'
β¦and then run clawhub install <slug> with whatever it prints.
2) Verify itβs there
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'ls -la /projects/_openclaw-workspace/skills'
docker exec -it openclaw-zs4q-openclaw-1 openclaw skills list
docker exec -it openclaw-zs4q-openclaw-1 openclaw skills check
3) Reload skills
Usually theyβre picked up on the next session, but the simplest is to restart the gateway container:
cd /docker/openclaw-zs4q
docker compose restart openclaw
Security note (important)
ClawHub skills are public thirdβparty code/instructions. Before using, open the installed SKILL.md and skim what it does + what tools it uses.
If you paste the output of openclaw skills list after install, I can tell you the exact command/name to invoke it from the UI.
openclaw-1 | 2026-02-16T02:07:34.911Z [ws] webchat connected conn=a5b4de4b-4f36-421b-a5f2-7ae158259510 remote=127.0.0.1 client=openclaw-control-ui webchat vdev
root@srv1100186:/docker/openclaw-zs4q# docker exec -it openclaw-zs4q-openclaw-1 sh -lc '
cd /projects/_openclaw-workspace
npx -y clawhub install theglove44/reddit --workdir /projects/_openclaw-workspace
'
β Skill not found
Error: Skill not found
root@srv1100186:/docker/openclaw-zs4q# docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'npx -y clawhub search "reddit" --limit 10'
reddit v1.0.0 Reddit (3.432)
reddit-readonly v1.0.0 Reddit (read only - no auth) (3.411)
reddit-cli v1.0.2 Reddit Cli (3.410)
reddit-scraper v1.0.0 Reddit Scraper (3.395)
reddit-insights v1.2.0 Reddit Insights (3.385)
reddit-read-only v1.0.0 Reddit (read only - no auth) (3.382)
search-reddit v1.0.0 Search Reddit (3.348)
reddit-api v1.2.0 Reddit Search (3.247)
reddit-explore v0.0.3 Reddit Explore (3.200)
reddit-researcher v1.0.1 Reddit Researcher (3.113)
root@srv1100186:/docker/openclaw-zs4q#
the skill is availble but it's marked as suspicious but I checked and it isn't
theglove44/reddit is the web URL, but the ClawHub CLI installs by slug, and in your search results the slugs are things like reddit, reddit-readonly, etc. Thatβs why clawhub install theglove44/reddit says βSkill not foundβ.
1) Pick one from the search list (Iβd start with read-only)
On the VPS:
docker exec -it openclaw-zs4q-openclaw-1 sh -lc '
cd /projects/_openclaw-workspace
npx -y clawhub install reddit-readonly --workdir /projects/_openclaw-workspace
'
(Or if you want the main one: replace reddit-readonly with reddit.)
2) Inspect what you just installed (important if itβs marked suspicious)
docker exec -it openclaw-zs4q-openclaw-1 sh -lc '
ls -la /projects/_openclaw-workspace/skills
sed -n "1,200p" /projects/_openclaw-workspace/skills/reddit-readonly/SKILL.md
'
If ClawHub marks something βsuspiciousβ, it usually means it got reported/flagged. You canβt βun-suspiciousβ it locally β the right move is to audit the skill files and only proceed if youβre comfortable.
3) Restart OpenClaw so it picks up the new workspace skill
cd /docker/openclaw-zs4q
docker compose restart openclaw
4) Confirm OpenClaw sees it
docker exec -it openclaw-zs4q-openclaw-1 openclaw skills list
docker exec -it openclaw-zs4q-openclaw-1 openclaw skills check
If you tell me which one you actually want (reddit vs reddit-readonly vs reddit-cli), Iβll give you the exact install + how to invoke it once itβs loaded.
I want to install the one I found the link is availble
That link format (/theglove44/reddit) is the web URL (user + page), but the CLI installs by slug. From your clawhub search output, the slug you want is almost certainly just:
reddit(orreddit-readonlyif you want the safer read-only one)
Install the one from the link (slug: reddit)
Run on the VPS:
docker exec -it openclaw-zs4q-openclaw-1 sh -lc '
cd /projects/_openclaw-workspace
npx -y clawhub install reddit --workdir /projects/_openclaw-workspace
'
Verify what you installed (so you know itβs really that one)
docker exec -it openclaw-zs4q-openclaw-1 sh -lc '
ls -la /projects/_openclaw-workspace/skills
sed -n "1,120p" /projects/_openclaw-workspace/skills/reddit/SKILL.md
cat /projects/_openclaw-workspace/.clawhub/lock.json 2>/dev/null | head -80
'
Reload skills
cd /docker/openclaw-zs4q
docker compose restart openclaw
Then:
docker exec -it openclaw-zs4q-openclaw-1 openclaw skills list
docker exec -it openclaw-zs4q-openclaw-1 openclaw skills check
About the βsuspiciousβ flag: thatβs a ClawHub moderation/report signal. You can still use it if youβve reviewed SKILL.md + any scripts it calls and youβre comfortableβbut treat it as untrusted until youβve read it.
If clawhub install reddit errors, paste the exact error line and Iβll tell you what it needs.
cd /projects/_openclaw-workspace
npx -y clawhub install reddit --workdir /projects/_openclaw-workspace
'
Error: Already installed: /projects/_openclaw-workspace/skills/reddit (use --force)
root@srv1100186:/docker/openclaw-zs4q#
shouldn't it go in the skills.md folder?
It did go into the right place.
ClawHub installs skills as folders under:
/projects/_openclaw-workspace/skills/<skill-name>/
So skills/reddit/ is correct β there is no single βskills.md folderβ. (The file is SKILL.md inside the skill folder.)
The error just means itβs already there.
Verify itβs installed
ls -la /projects/_openclaw-workspace/skills
ls -la /projects/_openclaw-workspace/skills/reddit
sed -n '1,120p' /projects/_openclaw-workspace/skills/reddit/SKILL.md
If you want to overwrite/update it anyway
npx -y clawhub install reddit --workdir /projects/_openclaw-workspace --force
(or, better, update all installed skills:)
npx -y clawhub update --all --workdir /projects/_openclaw-workspace
Make OpenClaw pick it up
Restart OpenClaw (or start a new session):
cd /docker/openclaw-zs4q
docker compose restart openclaw
Then check:
docker exec -it openclaw-zs4q-openclaw-1 openclaw skills list
'
β Skill not found
Error: Skill not found
root@srv1100186:/docker/openclaw-zs4q# docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'npx -y clawhub search "reddit" --limit 10'
reddit v1.0.0 Reddit (3.432)
reddit-readonly v1.0.0 Reddit (read only - no auth) (3.411)
reddit-cli v1.0.2 Reddit Cli (3.410)
reddit-scraper v1.0.0 Reddit Scraper (3.395)
reddit-insights v1.2.0 Reddit Insights (3.385)
reddit-read-only v1.0.0 Reddit (read only - no auth) (3.382)
search-reddit v1.0.0 Search Reddit (3.348)
reddit-api v1.2.0 Reddit Search (3.247)
reddit-explore v0.0.3 Reddit Explore (3.200)
reddit-researcher v1.0.1 Reddit Researcher (3.113)
root@srv1100186:/docker/openclaw-zs4q# docker exec -it openclaw-zs4q-openclaw-1 sh -lc '
cd /projects/_openclaw-workspace
npx -y clawhub install reddit --workdir /projects/_openclaw-workspace
'
Error: Already installed: /projects/_openclaw-workspace/skills/reddit (use --force)
root@srv1100186:/docker/openclaw-zs4q# ls -la /projects/_openclaw-workspace/skills
ls -la /projects/_openclaw-workspace/skills/reddit
sed -n '1,120p' /projects/_openclaw-workspace/skills/reddit/SKILL.md
ls: cannot access '/projects/_openclaw-workspace/skills': No such file or directory
ls: cannot access '/projects/_openclaw-workspace/skills/reddit': No such file or directory
sed: can't read /projects/_openclaw-workspace/skills/reddit/SKILL.md: No such file or directory
root@srv1100186:/docker/openclaw-zs4q#
You did install it β youβre just looking in the wrong place.
/projects/... exists inside the OpenClaw container (itβs a Docker bind mount). On the VPS host, the same files live under:
/docker/openclaw-zs4q/projects/...
Thatβs why your host-side ls -la /projects/_openclaw-workspace/skills says βNo such fileβ.
Check it (inside the container)
Run on the VPS:
docker exec -it openclaw-zs4q-openclaw-1 sh -lc '
ls -la /projects/_openclaw-workspace
ls -la /projects/_openclaw-workspace/skills
ls -la /projects/_openclaw-workspace/skills/reddit
sed -n "1,120p" /projects/_openclaw-workspace/skills/reddit/SKILL.md
'
Or check it (host path)
ls -la /docker/openclaw-zs4q/projects/_openclaw-workspace
ls -la /docker/openclaw-zs4q/projects/_openclaw-workspace/skills
That earlier ββ Skill not foundβ
That was for theglove44/reddit (the web URL). The CLI installs by slug (like reddit, reddit-readonly, etc.), not by the web URL.
If you want, paste the first ~30 lines of SKILL.md after the command above and Iβll confirm which βredditβ skill you actually installed and how to invoke it from OpenClaw.
Get Comments
# By post ID or full URL
node /projects/_openclaw-workspace/scripts/reddit.mjs comments t3_abc123
node /projects/_openclaw-workspace/scripts/reddit.mjs comments "https://reddit.com/r/wallstreetbets/comments/abc123/..."
Submit a Post (requires auth)
# Text post
node /projects/_openclaw-workspace/scripts/reddit.mjs submit yoursubreddit --title "Weekly Discussion" --text "What's on your mind?"
# Link post
node /projects/_openclaw-workspace/scripts/reddit.mjs submit yoursubreddit --title "Great article" --url "https://example.com"
Reply to Post/Comment (requires auth)
node /projects/_openclaw-workspace/scripts/reddit.mjs reply t1_abc123 "Your reply text here"
Moderation (requires auth + mod permissions)
# Remove a post/comment
node /projects/_openclaw-workspace/scripts/reddit.mjs mod remove t3_abc123
# Approve a post/comment
node /projects/_openclaw-workspace/scripts/reddit.mjs mod approve t3_abc123
# Sticky a post
node /projects/_openclaw-workspace/scripts/reddit.mjs mod sticky t3_abc123
# Unsticky
node /projects/_openclaw-workspace/scripts/reddit.mjs mod unsticky t3_abc123
# Lock comments
node /projects/_openclaw-workspace/scripts/reddit.mjs mod lock t3_abc123
# View modqueue
node /projects/_openclaw-workspace/scripts/reddit.mjs mod queue yoursubreddit
Implementation
The script uses:
- Public JSON API for read actions (no auth needed)
- OAuth for posting/moderation (via password grant)
- Automatic token refresh when expired
Rate Limits
- ~60 requests/minute for OAuth
- ~10 requests/minute for unauthenticated
Notes
- Read actions work without any auth
- Post/mod actions require OAuth credentials set as environment variables
- Token is stored in memory (not persisted between sessions)
- For production use, consider using a proper OAuth flow with refresh tokens
root@srv1100186:/docker/openclaw-zs4q#
Yep β that confirms the skill is installed correctly. Itβs basically a Reddit CLI script at:
/projects/_openclaw-workspace/scripts/reddit.mjs
A couple important points for using it with OpenClaw:
1) Read-only stuff will work immediately (no auth)
You can ask OpenClaw to run commands like βget comments for this URLβ, and it should call:
node /projects/_openclaw-workspace/scripts/reddit.mjs comments "<reddit url>"
2) Posting/mod actions need credentials (donβt paste them in chat)
Anything like submit, reply, mod ... requires OAuth env vars. Best practice is:
- put them in your OpenClaw config under
skills.entries.reddit.envor - set them in the container environment (compose
.env) and restart
Before you do that, open the top of the skillβs SKILL.md (first ~50 lines) to see the exact required env var names:
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'sed -n "1,80p" /projects/_openclaw-workspace/skills/reddit/SKILL.md'
3) Make sure OpenClaw actually βseesβ the skill
After installing, restart OpenClaw once:
cd /docker/openclaw-zs4q
docker compose restart openclaw
Then:
docker exec -it openclaw-zs4q-openclaw-1 openclaw skills list
docker exec -it openclaw-zs4q-openclaw-1 openclaw skills check
If you tell me whether you want read-only Reddit research or posting/moderation, Iβll give you the safest setup for credentials (without leaking secrets).
I need to reauthenicate a new device browser on tailnet how do I do this?
To re-auth a new browser/device for the OpenClaw Control UI, you need to approve the pairing request on the VPS.
-
Open the Control UI from the new browser (so it generates a pairing request):
https://srv1100186.tail777ba9.ts.net/ -
On the VPS, list pending device requests:
docker exec -it openclaw-zs4q-openclaw-1 openclaw devices list
- Approve the new request (copy the
requestIdfrom the output):
docker exec -it openclaw-zs4q-openclaw-1 openclaw devices approve <requestId>
- Refresh the UI page.
If devices list shows nothing pending, refresh the UI once and run devices list again. If you paste the devices list output, Iβll point to the exact requestId.
{
"status": "error",
"tool": "browser",
"error": "Can't reach the OpenClaw browser control service (timed out after 15000ms). Start (or restart) the OpenClaw gateway (OpenClaw.app menubar, or openclaw gateway) and try again."
}
i want it to take over my browser and start working but it doesn't seem to do anything
Clawdbot Browser Relay
Click the toolbar button on a tab to attach / detach.
Getting started
If you see a red ! badge on the extension icon, the relay server is not reachable. Start Clawdbotβs browser relay on this machine (Gateway or clawdbot browser serve), then click the toolbar button again.
Full guide (install, remote Gateway, security): docs.clawd.bot/tools/chrome-extension
Relay port
Port
18792
Save
Default: 18792. Extension connects to: http://127.0.0.1:18792/. Only change this if your Clawdbot profile uses a different cdpUrl port.
Relay not reachable at http://127.0.0.1:18792/. Start Clawdbotβs browser relay on this machine, then click the toolbar button again
do I change somethign here?
That error happens because your Gateway is on the VPS, but the browser you want to βtake overβ is on your Mac. The browser tool can only control a browser on the same machine (or via a node host on that machine).
To get βtake over my browserβ working from a VPS Gateway, do this:
A) Run a node host on your Mac (this is what gives the Gateway access to your Macβs browser)
- On the VPS, get your gateway token (donβt paste it here):
docker exec -it openclaw-zs4q-openclaw-1 openclaw config get gateway.auth.token
- On your Mac, install the OpenClaw CLI (pick one):
npm i -g openclaw
# or
pnpm add -g openclaw
- On your Mac, start the node host (connect through your Tailscale Serve HTTPS):
export OPENCLAW_GATEWAY_TOKEN="<paste-token-here>"
openclaw node run --host srv1100186.tail777ba9.ts.net --port 443 --tls --display-name "Mac Browser Node"
- On the VPS, approve the node pairing request:
docker exec -it openclaw-zs4q-openclaw-1 openclaw nodes pending
docker exec -it openclaw-zs4q-openclaw-1 openclaw nodes approve <requestId>
B) Install the Browser Relay extension on your Macβs Chrome/Brave and attach a tab
On your Mac:
openclaw browser extension install
openclaw browser extension path
Then in Chrome:
- open
chrome://extensions - enable Developer mode
- Load unpacked β select the folder from
openclaw browser extension path - click the OpenClaw Browser Relay icon on the tab you want controlled (badge should show ON)
C) Use it
Now, when you ask OpenClaw to use the browser tool, it should route to your Mac node and control the attached tab (profile is usually chrome / relay).
If you tell me: Mac browser = Chrome or Brave? and whether you already have openclaw installed on the Mac, I can tighten the commands to exactly your setup.
I am on windows
Donβt change anything in that extension page (leave Port = 18792).
That Relay not reachable at http://127.0.0.1:18792/ means: there is no OpenClaw relay server running on your Mac. Since your Gateway is on the VPS, the only way to βtake over your Mac browserβ is to run a node host on your Mac (the node provides the local relay + browser proxy).
1) Get your Gateway token (on the VPS) β donβt paste it here
docker exec -it openclaw-zs4q-openclaw-1 openclaw config get gateway.auth.token
2) On your Mac: install OpenClaw CLI + start a node host
npm i -g openclaw
# or: pnpm add -g openclaw
export OPENCLAW_GATEWAY_TOKEN="<token from step 1>"
openclaw node run --host srv1100186.tail777ba9.ts.net --port 443 --tls --display-name "Mac Browser"
Keep that running.
3) Approve the node on the VPS
docker exec -it openclaw-zs4q-openclaw-1 openclaw nodes pending
docker exec -it openclaw-zs4q-openclaw-1 openclaw nodes approve <requestId>
4) Back in Chrome on your Mac
- The extension
!should go away (relay becomes reachable) - Click the toolbar button on the tab you want β badge
ON
After that, OpenClaw can drive the attached tab (browser profile usually chrome).
If you tell me what happens after you start openclaw node run on the Mac (any errors / does the ! badge disappear?), Iβll guide the next step.
Got it β same idea, just βnode host on Windowsβ instead of Mac. Donβt change the relay port (keep 18792).
1) Get the gateway token (on the VPS) β donβt post it
docker exec -it openclaw-zs4q-openclaw-1 openclaw config get gateway.auth.token
2) On Windows: install OpenClaw CLI
Open PowerShell (normal is fine) and run:
npm i -g openclaw
(If you donβt have Node/npm installed, install Node.js LTS first.)
3) On Windows: start the node host (keep it running)
In PowerShell:
$env:OPENCLAW_GATEWAY_TOKEN="PASTE_TOKEN_HERE"
openclaw node run --host srv1100186.tail777ba9.ts.net --port 443 --tls --display-name "Windows Browser"
4) Approve the node on the VPS
docker exec -it openclaw-zs4q-openclaw-1 openclaw nodes pending
docker exec -it openclaw-zs4q-openclaw-1 openclaw nodes approve <requestId>
5) In Chrome on Windows
- The extension
!should disappear once the node host is running - Click the extension button on the tab you want to control β badge
ON
If you paste the output of openclaw node run ... on Windows (just the error lines if any), Iβll tell you what to fix (common: firewall prompt, token missing, or TLS flag).
Microsoft Windows [Version 10.0.26200.7840]
(c) Microsoft Corporation. All rights reserved.
C:\Users>npm i -g openclaw
|
Let that npm i -g openclaw finish β it can sit there with that | spinner for a bit while it downloads/install.
1) When itβs done, verify the CLI is installed (on Windows)
In that same terminal:
openclaw --version
where openclaw
If openclaw isnβt found, paste the last few lines of the npm output (itβs usually a PATH or permissions issue).
2) Make sure youβre on Tailscale on this Windows machine
You need to be connected to the same tailnet, otherwise srv1100186.tail777ba9.ts.net wonβt be reachable.
If you have the Tailscale app installed, just confirm it says βConnectedβ.
(If you have the CLI: tailscale status.)
3) Start the node host on Windows (this is what makes the relay reachable)
Donβt paste the token in chat. In CMD:
set OPENCLAW_GATEWAY_TOKEN=PASTE_TOKEN_HERE
openclaw node run --host srv1100186.tail777ba9.ts.net --port 443 --tls --display-name "Windows Browser"
Leave that running.
4) Approve the node on the VPS
On the VPS:
docker exec -it openclaw-zs4q-openclaw-1 openclaw nodes pending
docker exec -it openclaw-zs4q-openclaw-1 openclaw nodes approve <requestId>
5) Back in Chrome (Windows)
Once the node host is running + approved, the extensionβs ! (βrelay not reachableβ) should go away. Then click the extension icon on the tab you want to control β badge ON.
If you get stuck, tell me exactly what openclaw --version prints (or the error) and whether Tailscale on Windows is connected.
\AppData\Local\Programs\Microsoft VS Code\bin;C:\Users\Dylan\AppData\Roaming\npm;C:\Users\Dylan\AppData\Local\Python\bin
node host gateway connect failed: unauthorized: gateway token missing (provide gateway auth token)
That error is exactly because the Windows node host connected without a gateway token.
Do this:
1) Get the Gateway token (on the VPS) β donβt paste it in Discord
docker exec -it openclaw-zs4q-openclaw-1 openclaw config get gateway.auth.token
2) On Windows: set the token in the same terminal and start the node host
If youβre in CMD (C:\Users\...>):
set OPENCLAW_GATEWAY_TOKEN=PASTE_TOKEN_HERE
openclaw node run --host srv1100186.tail777ba9.ts.net --port 443 --tls --display-name "Windows Browser"
If youβre in PowerShell:
$env:OPENCLAW_GATEWAY_TOKEN="PASTE_TOKEN_HERE"
openclaw node run --host srv1100186.tail777ba9.ts.net --port 443 --tls --display-name "Windows Browser"
Leave it running.
3) Approve the node on the VPS
docker exec -it openclaw-zs4q-openclaw-1 openclaw nodes pending
docker exec -it openclaw-zs4q-openclaw-1 openclaw nodes approve <requestId>
4) Back in Chrome on Windows (Browser Relay extension)
You should not change the port (keep 18792). Once the node host is running, the extensionβs red ! should go away, then click the toolbar button on the tab β badge ON.
If it still says βrelay not reachableβ after the node is connected, tell me:
- are you using CMD or PowerShell?
- and paste the last ~10 lines from the
openclaw node run ...window (no secrets).
ppData\Local\Python\bin
node host gateway connect failed: pairing required
gateway connect failed: Error: pairing required
node host gateway closed (1005):
node:internal/modules/cjs/loader:1424
throw err;
^
Error: Cannot find module 'C:\Users\Dylan\host'
at Module._resolveFilename (node:internal/modules/cjs/loader:1421:15)
at defaultResolveImpl (node:internal/modules/cjs/loader:1059:19)
at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1064:22)
at Module._load (node:internal/modules/cjs/loader:1227:37)
at TracingChannel.traceSync (node:diagnostics_channel:328:14)
at wrapModuleLoad (node:internal/modules/cjs/loader:245:24)
at Module.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:154:5)
at node:internal/main/run_main_module:33:47 {
code: 'MODULE_NOT_FOUND',
requireStack: []
Two separate things happened:
pairing required= your Windows node is trying to connect, but the Gateway needs you to approve that node first.Cannot find module 'C:\Users\Dylan\host'= you (or the terminal) ran a broken command (usuallynode host ...or theopenclaw node run --host ...line got split/wrapped).
Step 1 β Approve the Windows node (run on the VPS)
This is node pairing (different from openclaw devices ...):
docker exec -it openclaw-zs4q-openclaw-1 openclaw nodes pending
Youβll see a requestId. Approve it:
docker exec -it openclaw-zs4q-openclaw-1 openclaw nodes approve <requestId>
Step 2 β Start the node host again on Windows (run locally on Windows)
Make sure you run openclaw node run (not node host), and keep it all on one line.
PowerShell:
$env:OPENCLAW_GATEWAY_TOKEN="PASTE_TOKEN_HERE"
openclaw node run --host srv1100186.tail777ba9.ts.net --port 443 --tls --display-name "Windows Browser"
CMD:
set OPENCLAW_GATEWAY_TOKEN=PASTE_TOKEN_HERE
openclaw node run --host srv1100186.tail777ba9.ts.net --port 443 --tls --display-name "Windows Browser"
If you still see Cannot find module ...\host, paste the exact command you ran (redact the token) β that error almost always means the command got mangled/split.
Step 3 β Browser Relay extension
Once the node host is connected, the extensionβs ! (βrelay not reachableβ) should disappear. Then click the extension icon on the tab β badge ON.
root@srv1100186:/docker/openclaw-zs4q# docker exec -it openclaw-zs4q-openclaw-1 openclaw nodes pending
π¦ OpenClaw 2026.2.12 (f9e444d) β OpenAI-compatible, not OpenAI-dependent.
β
β
No pending pairing requests.
Donβt change the port in the extension (leave 18792). Your problem right now is the Windows node host isnβt paired/approved yet, so it disconnects, and the relay never comes up.
Also: openclaw nodes pending is the wrong place for this one β the βpairing requiredβ youβre seeing is device pairing, so you approve it with openclaw devices β¦.
1) On Windows: run the node host again (with token)
Make sure you run openclaw node run (not node host β¦).
CMD:
set OPENCLAW_GATEWAY_TOKEN=PASTE_TOKEN_HERE
openclaw node run --host srv1100186.tail777ba9.ts.net --port 443 --tls --display-name "Windows Browser"
Keep that window open. If it prints pairing required, leave it.
2) On the VPS: approve the pairing request (devices)
Run:
docker exec -it openclaw-zs4q-openclaw-1 openclaw devices list
You should see a pending request. Approve it:
docker exec -it openclaw-zs4q-openclaw-1 openclaw devices approve <requestId>
3) Back on Windows
The node host should stay connected after approval.
At that point, the Chrome extensionβs ! (βrelay not reachableβ) should go away. Then:
- click the extension button on the tab you want to control β badge
ON
About this error you saw
Cannot find module 'C:\Users\Dylan\host' happens when Windows is running Node.js (node β¦) with host as an argument (wrong command). Stick to openclaw node run ... exactly.
If openclaw devices list still shows nothing pending, paste what the Windows openclaw node run ... window prints (redact the token).
ale;C:\Program Files\Docker\Docker\resources\bin;C:\Users\Dylan\AppData\Local\Microsoft\WindowsApps;C:\ffmepg;C:\Users\Dylan\AppData\Local\Programs\Microsoft VS Code\bin;C:\Users\Dylan\AppData\Roaming\npm;C:\Users\Dylan\AppData\Local\Python\bin
node host gateway connect failed: pairing required
gateway connect failed: Error: pairing required
node host gateway closed (1005):
openclaw nodes pending showing nothing is normal here β node hosts are approved via device pairing, so you need openclaw devices β¦.
Do this while your Windows openclaw node run β¦ is running and saying pairing required:
1) On the VPS: list pending device pairing requests
docker exec -it openclaw-zs4q-openclaw-1 openclaw devices list
You should see a pending request (often itβll mention role: node).
2) Approve it (on the VPS)
docker exec -it openclaw-zs4q-openclaw-1 openclaw devices approve <requestId>
3) Back on Windows
The openclaw node run β¦ process should connect successfully after approval. If it doesnβt auto-reconnect, just stop it and run it again.
If openclaw devices list shows no pending requests, paste the output of openclaw devices list and also the exact Windows command youβre running (redact the token).
π¦ OpenClaw 2026.2.14 (c1feda1) β Welcome to the command line: where dreams compile and confidence segfaults.
node host PATH: C:\ffmepg\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0;C:\WINDOWS\System32\OpenSSH;C:\Program Files\dotnet;C:\Users\Dylan\AppData\Local\Microsoft\WindowsApps;C:\Users\Dylan\go\bin;C:\ffmepg;C:\Program Files\nodejs;C:\Program Files\Git\cmd;C:\Program Files\Tailscale;C:\Program Files\Docker\Docker\resources\bin;C:\Users\Dylan\AppData\Local\Microsoft\WindowsApps;C:\ffmepg;C:\Users\Dylan\AppData\Local\Programs\Microsoft VS Code\bin;C:\Users\Dylan\AppData\Roaming\npm;C:\Users\Dylan\AppData\Local\Python\bin
shows this now and is blinking the cursor
That blinking cursor is good β it means the Windows node host is running in the foreground. Leave that window open.
Next step is to approve the node host via device pairing (not nodes pending):
1) On the VPS: approve the pending device (role: node)
docker exec -it openclaw-zs4q-openclaw-1 openclaw devices list
docker exec -it openclaw-zs4q-openclaw-1 openclaw devices approve <requestId>
Then confirm the node is connected:
docker exec -it openclaw-zs4q-openclaw-1 openclaw nodes status
2) Back on Windows Chrome extension
Once the node is paired/connected, the extensionβs red ! (βrelay not reachableβ) should go away.
If it still says relay not reachable, in a second Windows terminal run:
openclaw browser status
If it says not running, start it (this starts the local relay on 18792):
openclaw browser start --browser-profile openclaw
3) Attach the tab
Click the Browser Relay extension icon on the tab β badge ON.
If openclaw devices list shows nothing pending, paste its output (itβs safe) and Iβll tell you what to do next.
Paired (5)
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββ¬βββββββββββββ
β Device β Roles β Scopes β Tokens β IP β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββΌβββββββββββββ€
β Windows Browser β node
π¦ OpenClaw 2026.2.14 (c1feda1) β We ship features faster than Apple ships calculator updates.
|
o
Error: gateway closed (1006 abnormal closure (no close frame)): no close reason
Gateway target: ws://127.0.0.1:18789
Source: local loopback
Config: C:\Users\Dylan.openclaw\openclaw.json
Bind: loopback
Great β that output means your Windows node is paired (Roles: node). Now you just need to get the local relay server running on your Windows box so the Chrome extension can reach http://127.0.0.1:18792/.
On Windows (keep openclaw node run ... running in one terminal)
Open a second terminal on Windows and run:
- Check what port/profile it expects:
openclaw browser profiles
Look for the chrome profile and its cdpUrl (usually http://127.0.0.1:18792).
If itβs a different port, set the extension Options port to match.
- Start the browser control service (this is what makes the relay reachable):
openclaw browser --browser-profile openclaw start
(That may open a separate βopenclawβ browser window β thatβs fine.)
- Now the extension Options page should say the relay is reachable. Go to the tab you want controlled and click the extension icon β badge ON.
Quick sanity check (Windows)
openclaw browser --browser-profile chrome tabs
If it lists tabs / doesnβt error, the relay is working.
If the extension still shows ! relay not reachable, paste the output of openclaw browser profiles (itβs safe) and Iβll tell you exactly what port to set in the extension.
C:\Users\Dylan>openclaw browser profiles
π¦ OpenClaw 2026.2.14 (c1feda1) β The only crab in your contacts you actually want to hear from. π¦
|
o
Error: gateway closed (1006 abnormal closure (no close frame)): no close reason
Gateway target: ws://127.0.0.1:18789
Source: local loopback
Config: C:\Users\Dylan.openclaw\openclaw.json
Bind: loopback
That Gateway target: ws://127.0.0.1:18789 error is because your Windows OpenClaw CLI is trying to talk to a Gateway on your Windows PC. But your real Gateway is on the VPS (srv1100186...ts.net).
You already paired the Windows node (good). Now you just need to run the browser command against the remote Gateway, so it can spin up the relay on your Windows node.
Do this on Windows (2nd terminal, while openclaw node run ... keeps running)
Option A (one-off, no config changes)
In CMD:
set OPENCLAW_GATEWAY_TOKEN=PASTE_TOKEN_HERE
openclaw browser --url wss://srv1100186.tail777ba9.ts.net/ --browser-profile chrome tabs
(or PowerShell:)
$env:OPENCLAW_GATEWAY_TOKEN="PASTE_TOKEN_HERE"
openclaw browser --url wss://srv1100186.tail777ba9.ts.net/ --browser-profile chrome tabs
If it works, youβll usually see something like βrelay running but no tab is connectedβ (thatβs fine).
Option B (set it once so you donβt need --url every time)
openclaw config set gateway.remote.url "wss://srv1100186.tail777ba9.ts.net/"
Then you can just run:
openclaw browser --browser-profile chrome tabs
(with OPENCLAW_GATEWAY_TOKEN still set in the terminal)
Then in Chrome (Windows)
- Keep extension port 18792
- The red
!should go away once the relay is up - Click the extension icon on the tab you want controlled β badge ON
If openclaw browser ... tabs still errors, paste the exact error line (redact token).
That error is because your Windows openclaw CLI is still trying to talk to a local gateway at ws://127.0.0.1:18789 (which you donβt have). You need to point the Windows CLI at your VPS gateway.
Fix it (Windows)
Option A: oneβoff command (no config changes)
Run this on Windows (PowerShell or CMD). Donβt paste the token here.
- Get the gateway token on the VPS:
docker exec -it openclaw-zs4q-openclaw-1 openclaw config get gateway.auth.token
- On Windows, run:
openclaw browser --url wss://srv1100186.tail777ba9.ts.net/ --token PASTE_TOKEN_HERE profiles
If that works, try:
openclaw browser --url wss://srv1100186.tail777ba9.ts.net/ --token PASTE_TOKEN_HERE --browser-profile chrome tabs
This usually makes the relay reachable for the extension.
Option B: set it once (recommended)
On Windows:
openclaw config set gateway.remote.url "wss://srv1100186.tail777ba9.ts.net/"
openclaw config set gateway.remote.transport "direct"
Then you can do:
openclaw browser --token PASTE_TOKEN_HERE profiles
If you get βpairing requiredβ after this
Approve the new device on the VPS:
docker exec -it openclaw-zs4q-openclaw-1 openclaw devices list
docker exec -it openclaw-zs4q-openclaw-1 openclaw devices approve <requestId>
Once openclaw browser β¦ --browser-profile chrome tabs works, the Chrome extensionβs red ! should go away and you can click the toolbar button to attach the tab (badge ON).
PS C:\WINDOWS\system32> $env:OPENCLAW_GATEWAY_TOKEN=""
PS C:\WINDOWS\system32> openclaw browser --url wss://srv1100186.tail777ba9.ts.net/ --browser-profile chrome tabs
π¦ OpenClaw 2026.2.14 (c1feda1) β I'm not saying your workflow is chaotic... I'm just bringing a linter and a helmet.
|
o
Error: gateway url override requires explicit credentials
Fix: pass --token or --password (or gatewayToken in tools).
Config: C:\Users\Dylan.openclaw\openclaw.json
PS C:\WINDOWS\system32>
That error is because you used --url ... and when you override the gateway URL, OpenClaw wonβt fall back to saved creds β you must pass credentials explicitly.
You cleared your token ($env:OPENCLAW_GATEWAY_TOKEN=""), so the CLI has nothing to auth with.
Option A (quick oneβoff)
On Windows PowerShell (token comes from your VPS; donβt paste it in Discord):
openclaw browser --url wss://srv1100186.tail777ba9.ts.net/ --token "PASTE_TOKEN_HERE" --browser-profile chrome tabs
Option B (recommended: set remote gateway once, then no --url needed)
On Windows PowerShell:
openclaw config set gateway.remote.url "wss://srv1100186.tail777ba9.ts.net/"
openclaw config set gateway.remote.transport "direct"
openclaw config set gateway.remote.token "PASTE_TOKEN_HERE"
Then run:
openclaw browser --browser-profile chrome tabs
If you get βpairing requiredβ
Approve it on the VPS:
docker exec -it openclaw-zs4q-openclaw-1 openclaw devices list
docker exec -it openclaw-zs4q-openclaw-1 openclaw devices approve <requestId>
Once ... chrome tabs works, your Chrome extension should stop showing ! (relay reachable), then click the toolbar icon on the tab β badge ON.
π¦ OpenClaw 2026.2.14 (c1feda1) β I speak fluent bash, mild sarcasm, and aggressive tab-completion energy.
|
gateway connect failed: Error: pairing required
o
Error: gateway closed (1005): no close reason
Gateway target: wss://srv1100186.tail777ba9.ts.net/
Source: cli --url
Config: C:\Users\Dylan.openclaw\openclaw.json
PS C:\Users\Dylan>
I did A and keeps saying
pairing required here means your Windows CLI (this PowerShell session/device) needs to be approved on the Gateway (same βdevice pairingβ flow as the Control UI). Itβs separate from the node pairing you already did.
1) On the VPS: list pending device pairing requests
docker exec -it openclaw-zs4q-openclaw-1 openclaw devices list
You should see a pending request for a new device/browser/CLI. Approve it:
docker exec -it openclaw-zs4q-openclaw-1 openclaw devices approve <requestId>
2) Retry the Windows command
Back on Windows:
openclaw browser --url wss://srv1100186.tail777ba9.ts.net/ --token "PASTE_TOKEN_HERE" --browser-profile chrome tabs
If devices list shows no pending requests, then the request might be expiring before you approve it. In that case:
- run the Windows command again, then immediately run
devices liston the VPS and paste its output.
(You can paste the devices list output here; it shouldnβt contain secrets.)
--browser-profile chrome tabs
π¦ OpenClaw 2026.2.14 (c1feda1) β I'm the assistant your terminal demanded, not the one your sleep schedule requested.
|
o
Error: gateway timeout after 3000ms
Gateway target: wss://srv1100186.tail777ba9.ts.net/
Source: cli --url
Config: C:\Users\Dylan.openclaw\openclaw.json
PS C:\Users\Dylan>
approved again and again error
π¦ OpenClaw 2026.2.14 (c1feda1) β OpenAI-compatible, not OpenAI-dependent.
|
o
No tabs (browser closed or no targets).
PS C:\Users\Dylan>
gateway timeout after 3000ms means the CLI can reach the gateway, but the browser call isnβt completing fast enough (usually because the gateway is trying to reach your Windows nodeβs browser proxy/relay and itβs not ready yet).
Do this checklist:
1) Make sure the Windows node is actually online (VPS)
On the VPS run:
docker exec -it openclaw-zs4q-openclaw-1 openclaw nodes status
You should see βWindows Browserβ as online/connected. If itβs offline, keep openclaw node run ... running on Windows.
2) Retry the Windows command with a bigger timeout
On Windows PowerShell (use your real token):
openclaw browser --timeout 20000 --url wss://srv1100186.tail777ba9.ts.net/ --token "PASTE_TOKEN_HERE" --browser-profile chrome tabs
3) Check whether the relay port is listening on Windows
In a Windows terminal:
netstat -ano | findstr :18792
If nothing is listening, the extension will keep showing ! (not reachable).
4) Easiest way to βkickβ the relay from the gateway side (VPS)
Run this on the VPS (itβll try to talk to the Windows node and start the relay there):
docker exec -it openclaw-zs4q-openclaw-1 openclaw browser --timeout 20000 --browser-profile chrome tabs
Once the relay is up, the Chrome extension ! should disappear. Then click the extension icon on the tab you want β badge ON. Only attached tabs can be controlled.
If you paste the output of openclaw nodes status from the VPS, I can tell you immediately whether the gateway can βseeβ the Windows node or if itβs the relay/browser side timing out.
That output is actually progress.
No tabs (browser closed or no targets) usually means the relay is reachable, but you havenβt attached any tab yet (extension relay mode only exposes tabs you explicitly attach).
Do this on your Windows machine:
1) Open Chrome (or Brave/Edge) and load any page
Then click the OpenClaw Browser Relay extension icon on that tab.
- The badge should change to ON.
- If it still shows
!, the relay isnβt reachable yet (but your last command suggests it is).
2) Re-run the tabs command
openclaw browser --url wss://srv1100186.tail777ba9.ts.net/ --token "PASTE_TOKEN_HERE" --browser-profile chrome tabs
Now it should list at least that attached tab.
3) If you want OpenClaw to open its own browser (no extension)
Use the managed profile instead:
openclaw browser --url wss://srv1100186.tail777ba9.ts.net/ --token "PASTE_TOKEN_HERE" --browser-profile openclaw start
openclaw browser --url wss://srv1100186.tail777ba9.ts.net/ --token "PASTE_TOKEN_HERE" --browser-profile openclaw open https://example.com
Tell me: after you click the extension icon, does the badge show ON? If yes and tabs still shows none, paste the output of openclaw browser β¦ chrome tabs again.
π¦ OpenClaw 2026.2.14 (c1feda1) β The only crab in your contacts you actually want to hear from. π¦
|
o
No tabs (browser closed or no targets).
PS C:\Users\Dylan>
seems to close directly
PS C:\Users\Dylan> netstat -ano | findstr :18792
PS C:\Users\Dylan>
That βNo tabs β¦β isnβt an error β it just means no Chrome tab is attached to the relay yet, so there are no targets to list. The command will print that and exit immediately (itβs not supposed to stay open).
To make tabs show up:
1) Attach a tab in Chrome (Windows)
- Open Chrome and go to a normal webpage (e.g.
https://example.com). - Click the OpenClaw Browser Relay extension icon on that tab.
- The badge must show ON.
If it flips back / wonβt stay ON, the relay still isnβt reachable.
2) Check the relay is actually listening on Windows (port 18792)
In Windows terminal:
netstat -ano | findstr :18792
You should see a LISTENING entry. If you see nothing, the relay isnβt up yet.
3) Re-run tabs with a longer timeout (Windows)
(Use the same token you used before.)
openclaw browser --timeout 20000 --url wss://srv1100186.tail777ba9.ts.net/ --token "PASTE_TOKEN_HERE" --browser-profile chrome tabs
Once a tab is attached (badge ON), this should list it.
4) Sanity check: is the Windows node still connected?
On the VPS:
docker exec -it openclaw-zs4q-openclaw-1 openclaw nodes status
If the node is offline, the relay wonβt work.
Tell me two things and Iβll pinpoint it fast:
- What badge do you see on the extension icon right now (
ON,!, or nothing)? - Does
netstat -ano | findstr :18792show a listener?
Relay port
Port
18789
Save
Default: 18792. Extension connects to: http://127.0.0.1:18789/. Only change this if your Clawdbot profile uses a different cdpUrl port.
Relay not reachable at http://127.0.0.1:18789/. Start Clawdbotβs browser relay on this machine, then click the toolbar button again
netstat -ano | findstr :18792 - this didn't print anything
netstat showing nothing on 18792 means the relay server is not running on your Windows machine yet, so the Chrome extension canβt attach any tabs.
Now that your Windows node is paired, do this:
1) Confirm the Windows node is actually ONLINE (VPS)
On the VPS:
docker exec -it openclaw-zs4q-openclaw-1 openclaw nodes status
You should see βWindows Browserβ as connected/online. If itβs offline, keep openclaw node run ... running on Windows.
2) Enable browser proxy on the Windows node (Windows)
On Windows (PowerShell/CMD), check:
openclaw config get browser.enabled
openclaw config get nodeHost.browserProxy.enabled
Set them if needed:
openclaw config set browser.enabled true --json
openclaw config set nodeHost.browserProxy.enabled true --json
Then restart your Windows node host (stop it with Ctrl+C, then re-run your openclaw node run ... command).
3) Trigger the relay to start (Windows)
Run this (with your token + remote URL):
openclaw browser --timeout 20000 --url wss://srv1100186.tail777ba9.ts.net/ --token "PASTE_TOKEN_HERE" --browser-profile chrome tabs
Now re-check:
netstat -ano | findstr :18792
You should see it listening.
4) Attach a tab
Open Chrome β go to any page β click the OpenClaw Browser Relay extension icon β badge ON.
If you paste the output of openclaw nodes status (VPS) and openclaw config get browser.enabled (Windows), I can tell you exactly which step is failing.
paired but disconnected
No tabs (browser closed or no targets).
PS C:\Users\Dylan> openclaw config get nodeHost.browserProxy.enabled
openclaw config get browser.enabled
π¦ OpenClaw 2026.2.14 (c1feda1) β Chat automation for people who peaked at IRC.
Config path not found: nodeHost.browserProxy.enabled
π¦ OpenClaw 2026.2.14 (c1feda1) β Hot reload for config, cold sweat for deploys.
Config path not found: browser.enabled
PS C:\Users\Dylan>
No β donβt change it to 18789. Set it back to the default 18792.
- 18789 is the OpenClaw gateway port (on your VPS), not the local Chrome relay.
- The Chrome extension must talk to a local relay on your Windows PC at
http://127.0.0.1:18792/.
Do this now
1) In the Chrome extension options (on Windows)
- Set Relay port = 18792
- Save
2) Make sure the Windows node host is running (keep it open)
On Windows, you should have openclaw node run ... still running in a terminal. If you closed it, start it again.
3) On the VPS: confirm the node is online
docker exec -it openclaw-zs4q-openclaw-1 openclaw nodes status
4) Trigger the browser proxy/relay to start (from the VPS)
This usually βwakes upβ the relay on the Windows node:
docker exec -it openclaw-zs4q-openclaw-1 openclaw browser --timeout 20000 --browser-profile chrome tabs
5) Back on Windows: check the relay port is listening
netstat -ano | findstr :18792
Now you should see it listening.
6) Attach a tab
Open Chrome β go to a page β click the Browser Relay extension icon β badge ON.
If nodes status shows your Windows node is offline, the relay will never come upβtell me what openclaw nodes status prints.
Two key fixes here:
- Set the extension relay port back to 18792 (NOT 18789).
- Your Windows node is paired, but the relay server still isnβt running (thatβs why
netstat :18792shows nothing).
1) Fix the Chrome extension options (Windows)
In the extension Options page:
- Relay port = 18792
- Save
(18789 is your gateway port, not the local relay.)
2) Make sure the Windows node host is actually connected (not just βpairedβ)
On the VPS run:
docker exec -it openclaw-zs4q-openclaw-1 openclaw nodes status
You should see your Windows Browser node as online/connected.
If itβs offline: re-run openclaw node run ... on Windows and keep that terminal open.
3) Explicitly enable the browser proxy on Windows (your βConfig path not foundβ is OK)
βConfig path not foundβ just means it wasnβt set explicitly. Set it anyway:
On Windows PowerShell:
openclaw config set browser.enabled true --json
openclaw config set nodeHost.browserProxy.enabled true --json
Then restart the Windows node host (Ctrl+C the node host, then run it again with your token).
4) Trigger the relay to start (from the VPS)
Run:
docker exec -it openclaw-zs4q-openclaw-1 openclaw browser --timeout 20000 --browser-profile chrome tabs
5) Verify the relay is now listening on Windows
On Windows:
netstat -ano | findstr :18792
You should now see a LISTENING entry.
6) Attach a tab
Open Chrome β go to any page β click the Browser Relay extension icon on that tab β badge ON.
If you paste the output of openclaw nodes status (from the VPS), I can tell you immediately whether the node is actually connected (the most common reason the relay never starts).
I set the extension to 18792
paired Β· disconnected β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ΄βββββββββββ΄ββββββββββββββββββββββ΄ββββββββββββββββββββββββ΄βββββββββββββββ
root@srv1100186:/docker/openclaw-zs4q# docker exec -it openclaw-zs4q-openclaw-1 openclaw browser --timeout 20000 --browser-profile chrome tabs
π¦ OpenClaw 2026.2.12 (f9e444d) β No $999 stand required.
β
β
No tabs (browser closed or no targets).
root@srv1100186:/docker/openclaw-zs4q#
βpaired Β· disconnectedβ means: the node was approved once, but the Windows node host isnβt currently connected/running. The browser relay will not come up unless the node host is online.
Do this:
1) On Windows: start the node host again (and keep that window open)
(In PowerShell or CMD β donβt paste the token here.)
$env:OPENCLAW_GATEWAY_TOKEN="PASTE_TOKEN_HERE"
openclaw node run --host srv1100186.tail777ba9.ts.net --port 443 --tls --display-name "Windows Browser"
2) On the VPS: confirm itβs actually connected
docker exec -it openclaw-zs4q-openclaw-1 openclaw nodes status
You want it to show connected/online, not disconnected.
3) Only after the node is connected: try the relay again
Keep the extension set to 18792 (correct), then run on the VPS:
docker exec -it openclaw-zs4q-openclaw-1 openclaw browser --timeout 20000 --browser-profile chrome tabs
4) Attach a tab (required)
Open Chrome on Windows β go to any page β click the Browser Relay extension icon on that tab β badge must show ON.
Then re-run the β¦ chrome tabs command; it should list the attached tab.
If you paste the full output of openclaw nodes status (VPS side), I can tell you immediately whether the blocker is βnode host not runningβ vs βnode connected but no tab attachedβ.
connected now, how do I let it do things?
Now that the Windows node host is connected, there are two βlevelsβ of letting it do things:
A) Let it control your browser tab (Chrome extension relay)
- On Windows, open Chrome to a page youβre OK with the agent controlling.
- Click the OpenClaw Browser Relay extension icon on that tab β badge ON.
- In OpenClaw (Control UI / chat), tell it something like:
βOpen the browser and navigate to example.com, then click β¦β
If you want to verify the tab is attached, run on the VPS:
docker exec -it openclaw-zs4q-openclaw-1 openclaw browser --browser-profile chrome tabs
It should list at least 1 tab.
B) Let it run commands on your Windows machine (exec on the node)
This is gated by approvals/allowlists (for safety). First verify the node is visible:
docker exec -it openclaw-zs4q-openclaw-1 openclaw nodes status
Then, in the Control UI, you can tell OpenClaw:
βRun
whoamion the Windows node.β
If it asks for approval / gets blocked, thatβs expected β we can add an allowlist entry for specific safe commands.
Tell me which action you want first:
- βControl my browser to do Xβ
or - βRun Windows commands (install stuff, build, etc.)β
β¦and what exactly you want it to do.
it shows two connected tabs but how do I actually let it control the browser now?
If you can see 2 connected tabs, youβve already βlet itβ at the plumbing level. Now you just need to tell OpenClaw to use the browser tool (and make sure it targets the right tab).
1) Make sure the tab you want is attached (badge ON)
On that tab in Chrome, the extension badge should be ON.
2) In the OpenClaw Control UI chat, give a concrete browser task
Example prompt you can paste:
βUse the browser to go to https://example.com, click the first link you see, then take a screenshot.β
(or whatever you actually want done)
3) If it controls the wrong tab, focus the right one
On the VPS, list tabs so you can pick the targetId:
docker exec -it openclaw-zs4q-openclaw-1 openclaw browser --browser-profile chrome tabs
Then focus the one you want (replace <targetId>):
docker exec -it openclaw-zs4q-openclaw-1 openclaw browser --browser-profile chrome focus <targetId>
After focusing, ask it again in chat to continue.
4) Common gotcha: it wonβt control a tab unless itβs attached
If the badge isnβt ON, click the extension icon again on that tab.
If you paste the output of ... chrome tabs (just the list with targetIds/titles), Iβll tell you which focus command to run.
Browser keeps disconnecting
When it βkeeps disconnectingβ there are usually two different disconnects that get conflated:
- the Windows node host disconnects from the VPS (then the relay dies), or
- the Chrome extension tab detaches (then you get βNo tabsβ).
We can tell which one in 30 seconds:
1) Check if the Windows node is staying online (VPS)
Run on the VPS:
docker exec -it openclaw-zs4q-openclaw-1 openclaw nodes status
- If it shows disconnected/offline β keep
openclaw node run ...running on Windows (donβt close that terminal), and make sure Windows isnβt sleeping / Tailscale stays connected.
2) Check if the relay is still up on Windows (port 18792)
On Windows PowerShell:
netstat -ano | findstr :18792
- If this shows nothing when the problem happens β the relay is not running (usually because the node host dropped).
- If it is listening but you still see βNo tabsβ β the tab is not attached (click the extension icon again on the tab; badge must be ON).
3) Check the extension badge when it βdisconnectsβ
- Badge ! = relay not reachable (node/relay down)
- Badge disappears / not ON = tab got detached (reattach)
Quick βmake it more stableβ option (no extension)
If the extension relay keeps flaking, use the managed browser profile instead (it launches its own OpenClaw-controlled Chrome on the Windows node, usually more stable than attaching tabs):
docker exec -it openclaw-zs4q-openclaw-1 openclaw browser --timeout 20000 --browser-profile openclaw start
docker exec -it openclaw-zs4q-openclaw-1 openclaw browser --timeout 20000 --browser-profile openclaw open https://example.com
Paste:
openclaw nodes statusoutput (VPS), and- whether
netstat ... :18792shows a listener when it breaks,
and Iβll tell you exactly which of the two is happening.
β core v2026.2.14 Β· path: β paired Β· connected (38m ago) β browser, β
β Browser β β β C:\ffmepg\bin;C:β¦:\Users\Dylan\AppData\Local\Python\bin β β system
If the goal is βthis container (or another container) should be reachable over my tailnetβ, the most reliable pattern is: run tailscale/tailscale as a sidecar, then put your actual app container into the Tailscale containerβs network namespace.
1) The βsidecarβ docker-compose pattern (Linux host)
services:
ts-app:
image: tailscale/tailscale:latest
hostname: my-app
environment:
- TS_AUTHKEY=tskey-auth-... # ideally an ephemeral auth key
- TS_STATE_DIR=/var/lib/tailscale # persist state
volumes:
- ./ts-state:/var/lib/tailscale
devices:
- /dev/net/tun:/dev/net/tun
cap_add:
- NET_ADMIN
- SYS_MODULE
restart: unless-stopped
app:
image: nginx:alpine
network_mode: service:ts-app # <-- magic: shares network stack with tailscaled
depends_on:
- ts-app
Now anything app listens on (e.g. port 80) is on the Tailscale IP of ts-app.
Official guide (worth following): https://tailscale.com/blog/docker-tailscale-guide
Docker image notes: https://hub.docker.com/r/tailscale/tailscale
2) If you canβt use /dev/net/tun (common on Docker Desktop / locked-down hosts)
You can run Tailscale in userspace networking mode (no TUN), but itβs more limited (often you end up using proxies / port forwards rather than a βrealβ interface). The Docker Hub page describes the behavior/limits.
Quick clarification so I can point you to the exact setup
- What host are you on: Linux vs Docker Desktop (Mac/Windows)?
- Do you need inbound access (reach the service over tailnet), outbound (container egress via tailnet), or both?
- Is the goal one container only, or βmake multiple containers reachableβ (reverse proxy style)?
Answer those 3 and Iβll suggest the cleanest compose for your exact case.
Last login: Mon Feb 16 04:04:57 2026 from 169.254.0.1
root@srv1100186:~# gog auth add --services gmail,calendar,drive,contacts,sheets,docs
Command 'gog' not found, but there are 16 similar ones.
root@srv1100186:~# curl -sSL https://gogcli.sh/install | sh
sh: 2: Syntax error: newline unexpected
root@srv1100186:~# # Download latest release
wget https://github.com/steipete/gog/releases/latest/download/gog-linux-amd64 -O gog
chmod +x gog
sudo mv gog /usr/local/bin/
Then run:
gog auth credentials ./client_secret.json
gog auth add dylan.devrieze@naturecan.com --services gmail,calendar,drive,contacts,sheets,docs
--2026-02-16 04:23:26-- https://github.com/steipete/gog/releases/latest/download/gog-linux-amd64
Resolving github.com (github.com)... 140.82.121.3
Connecting to github.com (github.com)|140.82.121.3|:443... connected.
HTTP request sent, awaiting response... 404 Not Found
2026-02-16 04:23:26 ERROR 404: Not Found.
That 404 is just the wrong repo + wrong asset name.
- The project is
steipete/gogcli(notsteipete/gog) - Linux releases are published as a tarball, e.g.
gogcli_0.11.0_linux_amd64.tar.gz(notgog-linux-amd64) curl -sSL https://gogcli.sh/install | shfails becausehttps://gogcli.sh/installis currently 404, so youβre piping a 404 page intosh.
Install on linux/amd64 (current latest is v0.11.0)
VER=0.11.0
wget "https://github.com/steipete/gogcli/releases/download/v${VER}/gogcli_${VER}_linux_amd64.tar.gz"
tar -xzf "gogcli_${VER}_linux_amd64.tar.gz"
sudo install -m 0755 gog /usr/local/bin/gog
gog --help
If you want βalways latestβ without hardcoding the version
curl -fsSL https://api.github.com/repos/steipete/gogcli/releases/latest \
| grep browser_download_url \
| grep linux_amd64
Grab that URL and wget it.
If you paste the output of tar -tzf gogcli_*_linux_amd64.tar.gz | head, I can tell you exactly what path the gog binary is inside the archive (in case it isnβt at the top-level).
inside the openclaw docker container?
Yes β but donβt try to βinstall tailscale into the OpenClaw containerβ if you can avoid it. The clean pattern is: run Tailscale as a sidecar container, and put the OpenClaw container into the sidecarβs network namespace.
Recommended: Tailscale sidecar + OpenClaw shares its network
services:
ts-openclaw:
image: tailscale/tailscale:latest
hostname: openclaw
environment:
- TS_AUTHKEY=tskey-auth-... # preferably ephemeral
- TS_STATE_DIR=/var/lib/tailscale
volumes:
- ./ts-state:/var/lib/tailscale
devices:
- /dev/net/tun:/dev/net/tun
cap_add:
- NET_ADMIN
- SYS_MODULE
restart: unless-stopped
openclaw:
image: openclaw/openclaw:latest # whatever youβre using
network_mode: service:ts-openclaw # <-- key line
depends_on:
- ts-openclaw
volumes:
- ./openclaw-data:/data # example; your real mounts/env here
environment:
- NODE_ENV=production
restart: unless-stopped
Now OpenClaw βis onβ the Tailscale IP of ts-openclaw.
Two gotchas
- This needs a Linux host where you can pass
/dev/net/tunand addNET_ADMIN(Docker Desktop often makes this painful). - For inbound access: OpenClaw must be listening on 0.0.0.0 (not just 127.0.0.1) or you can publish it via
tailscale serveinside the Tailscale container.
If you tell me:
- host OS (Linux VPS?),
- which OpenClaw port youβre trying to reach (gateway/dashboard),
- inbound only vs inbound+outbound,
I can give you the exacttailscale serve/ port setup to make it reachable securely over tailnet.
i mean gog do I need to install this inside the docker container for it to work?
If you are running gog manually on the server β install it wherever youβre typing the command (the host).
If you want OpenClaw (running in Docker) to be able to run gog via its tool/skill (i.e. it calls gog ... under the hood) β then yes, gog must be available inside the same environment where the OpenClaw Gateway runs, because thatβs where command execution happens.
Youβve got 3 common options:
- Bake
goginto your OpenClaw image (best/cleanest; survives restarts/redeploys). - Install
goginside the running container (works, but youβll lose it if the container is recreated). - Bind-mount the
gogbinary into the container, e.g. mount/usr/local/bin/gogfrom the host into the container at the same path.
Also: OAuth inside a headless container usually means using gog auth add ... --manual (copy/paste flow), and you need to store/mount the gog config directory/credentials so it persists.
If you tell me how you run OpenClaw (docker compose? which image name?) I can give you the exact Dockerfile or compose mount lines.
help me set it up
If you want OpenClaw (running in Docker) to be able to use gog, then yes: gog must be installed and configured in the same container environment where OpenClaw runs (because thatβs where command execution happens).
Hereβs a clean, repeatable setup.
0) First: find what user/home OpenClaw runs as
Run this on the host:
docker exec -it openclaw sh -lc 'id; echo "HOME=$HOME"; which sh; ls -la $HOME || true'
(Replace openclaw with your container name.)
This tells us where to mount the gog config so it persists.
1) Install gog into the OpenClaw container (two good options)
Option A (best): build a tiny custom image
Create a Dockerfile next to your compose:
FROM openclaw/openclaw:latest
ARG GOG_VER=0.11.0
ARG TARGETARCH=amd64
RUN set -eux; \
apt-get update && apt-get install -y --no-install-recommends ca-certificates curl tar && rm -rf /var/lib/apt/lists/*; \
curl -fsSL -o /tmp/gog.tgz "https://github.com/steipete/gogcli/releases/download/v${GOG_VER}/gogcli_${GOG_VER}_linux_${TARGETARCH}.tar.gz"; \
tar -xzf /tmp/gog.tgz -C /usr/local/bin gog; \
rm -f /tmp/gog.tgz; \
/usr/local/bin/gog --version
Then in docker-compose.yml:
services:
openclaw:
build: .
Option B (quick): bind-mount the host binary into the container
- Install
gogon the host at/usr/local/bin/gog - Add to compose:
services:
openclaw:
volumes:
- /usr/local/bin/gog:/usr/local/bin/gog:ro
2) Persist gogβs config + tokens (important)
In containers you usually want the file keyring backend (not OS keychain).
Add env + a volume mount (path depends on HOME from step 0):
services:
openclaw:
environment:
- GOG_KEYRING_BACKEND=file
- GOG_KEYRING_PASSWORD=${GOG_KEYRING_PASSWORD}
- GOG_ACCOUNT=you@gmail.com
volumes:
- ./gog-config:/root/.config/gog # adjust /root to whatever HOME was
Put GOG_KEYRING_PASSWORD=... in a .env file (donβt paste it into Discord).
3) Do the OAuth setup (headless / manual flow)
Copy your Google βDesktop appβ OAuth client JSON onto the server (again: donβt paste it into chat), then run inside the container:
docker exec -it openclaw sh -lc '
gog --version
gog auth keyring
'
Then store the client JSON (example assumes you mounted it somewhere like /data/client_secret.json):
docker exec -it openclaw sh -lc 'gog auth credentials /data/client_secret.json'
Then authorize your account (manual/headless flow):
docker exec -it openclaw sh -lc 'gog auth add you@gmail.com --services gmail,calendar,drive,contacts,sheets,docs --manual'
It will print a URL β open it on your local machine β approve β copy the full redirect URL back into the terminal prompt.
4) Test
docker exec -it openclaw sh -lc 'gog auth list --check && gog gmail labels list --max 5'
Paste the output of step 0 (id + HOME=...) and tell me whether you use docker compose and what your service/container name is, and Iβll give you the exact docker-compose.yml snippet (with the correct config mount path) for your setup.
root@srv1100186:~# docker exec -it openclaw sh -lc 'id; echo "HOME=$HOME"; which sh; ls -la $HOME || true'
Error response from daemon: No such container: openclaw
root@srv1100186:~#
Youβre just using the wrong container name. Letβs identify it first.
Run:
docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}'
If youβre using Compose, also run (from the directory with your docker-compose.yml):
docker compose ps
Then pick the container thatβs the OpenClaw gateway (often named something like clawdbot-gateway-1, openclaw-gateway-1, etc.) and rerun:
docker exec -it <NAME_FROM_docker_ps> sh -lc 'id; echo "HOME=$HOME"; which sh; ls -la $HOME || true'
Paste the output of docker ps β¦ and Iβll tell you exactly which one to target and what to mount for ./gog-config.
root@srv1100186:~# docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}'
NAMES IMAGE STATUS
openclaw-zs4q-openclaw-1 ghcr.io/hostinger/hvps-openclaw:latest Up 2 hours
root@srv1100186:~# docker compose ps
no configuration file provided: not found
root@srv1100186:~#
Got it β your container is:
openclaw-zs4q-openclaw-1 (image ghcr.io/hostinger/hvps-openclaw:latest)
So use that name in all the commands.
1) Inspect user + HOME inside the container
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'id; echo "HOME=$HOME"; ls -la "$HOME" || true'
Paste the output (mainly HOME=...).
2) Quickest path: mount the gog binary into the container
A) Install gog on the host (your VPS)
Download the correct linux/amd64 tarball and install:
VER=0.11.0
wget "https://github.com/steipete/gogcli/releases/download/v${VER}/gogcli_${VER}_linux_amd64.tar.gz"
tar -xzf "gogcli_${VER}_linux_amd64.tar.gz"
install -m 0755 gog /usr/local/bin/gog
gog --version
B) Restart the OpenClaw container with a bind mount
Since youβre not using docker compose, we need to see how this container is started (so we can add mounts). Run:
docker inspect openclaw-zs4q-openclaw-1 --format '{{json .HostConfig.Binds}}' | jq
If this container is managed by some Hostinger UI, youβll likely need to add mounts there. What we want to add:
/usr/local/bin/gog:/usr/local/bin/gog:ro
- a persistent config dir, e.g.
/root/.config/gog(or whatever HOME is) mapped to something on the host like/root/gog-config
Example binds:
/root/gog-config:/root/.config/gog/usr/local/bin/gog:/usr/local/bin/gog:ro
3) Configure gog for headless container usage
Once the binary + config mount are in place, run inside the container:
docker exec -it openclaw-zs4q-openclaw-1 sh -lc '
gog --version
export GOG_KEYRING_BACKEND=file
export GOG_KEYRING_PASSWORD="set-this-via-env-not-here"
gog auth keyring file
'
Then youβll do:
gog auth credentials /path/to/client_secret.jsongog auth add you@... --manual
(Weβll pick the right paths once we know where youβll store/mount the client secret JSON.)
Next thing I need from you
- Output of:
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'id; echo "HOME=$HOME"'
- How are you starting/managing this container (Hostinger panel? a systemd unit?
docker run ...somewhere)? If you run:
docker inspect openclaw-zs4q-openclaw-1 --format '{{json .Config.Cmd}} {{json .Config.Entrypoint}}'
and paste it, I can tell you the cleanest way to add the required mounts/env vars.
root@srv1100186:~# docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'id; echo "HOME=$HOME"; ls -la "$HOME" || true'
uid=0(root) gid=0(root) groups=0(root)
HOME=/data
total 32
drwxr-xr-x 8 node node 4096 Feb 15 09:23 .
drwxr-xr-x 1 root root 4096 Feb 16 03:07 ..
drwxr-xr-x 8 node node 4096 Feb 15 09:23 .cache
drwx------ 5 node node 4096 Feb 16 05:04 .config
drwxr-xr-x 5 node node 4096 Feb 16 03:11 .npm
drwx------ 13 node node 4096 Feb 16 03:39 .openclaw
drwx------ 3 node node 4096 Feb 15 07:04 .pki
drwxr-xr-x 3 node node 4096 Feb 15 06:16 linuxbrew
root@srv1100186:~#
Yep β since your OpenClaw containerβs HOME is /data (and thatβs clearly a persistent volume), the easiest setup is:
- put the
gogbinary into/data/bin/gog(so it survives container rebuilds), - store gog config/tokens under
/data/.config/gog(default), - use file keyring inside the container (no OS keychain),
- make sure OpenClaw can find
gog(either call/data/bin/gogor add/data/bintoPATH).
Below is a βworks even without docker composeβ recipe.
1) Confirm the container runs as node (recommended)
Run:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'id; echo "HOME=$HOME"'
If that works, do the rest as -u node so you donβt end up with root-owned files under /data that the app canβt read later.
2) Install gog into /data/bin (persistent)
Check CPU arch first:
uname -m
x86_64β uselinux_amd64aarch64/arm64β uselinux_arm64
x86_64 / amd64 install:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc '
set -eu
mkdir -p /data/bin
cd /tmp
VER=0.11.0
curl -fsSL -o gog.tgz "https://github.com/steipete/gogcli/releases/download/v${VER}/gogcli_${VER}_linux_amd64.tar.gz"
tar -xzf gog.tgz gog
mv gog /data/bin/gog
chmod +x /data/bin/gog
/data/bin/gog --version
'
(If youβre on arm64, swap the URL to ..._linux_arm64.tar.gz.)
3) Put your Google OAuth client JSON into the container volume
Copy your downloaded βDesktop appβ client JSON to the server, e.g.:
/data/client_secret.json(inside container)
You can place it on the host at the containerβs mounted /data path (Hostinger usually maps it), or docker cp it:
docker cp ./client_secret.json openclaw-zs4q-openclaw-1:/data/client_secret.json
4) Configure keyring + do headless auth (--manual)
Run:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc '
export GOG_KEYRING_BACKEND=file
# You MUST set this in your container env long-term; for now you can type it interactively:
# export GOG_KEYRING_PASSWORD="..."
/data/bin/gog auth keyring file
/data/bin/gog auth credentials /data/client_secret.json
/data/bin/gog auth add you@yourdomain.com --services gmail,calendar,drive,contacts,sheets,docs --manual
'
It will print a URL:
- open it on your local machine
- approve
- copy the full redirect URL from the browser address bar
- paste it back into the terminal prompt
Test:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc '
export GOG_KEYRING_BACKEND=file
/data/bin/gog auth list --check
/data/bin/gog gmail labels list --max 5
'
5) Make it work for OpenClaw βfor realβ (non-interactive)
For OpenClaw to run gog automatically, youβll want these environment variables set on the container (via whatever Hostinger uses to configure the container):
GOG_KEYRING_BACKEND=fileGOG_KEYRING_PASSWORD=...(donβt paste it into Discord)- optionally
GOG_ACCOUNT=you@... - optionally
PATH=/data/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin(sogogis found without absolute path)
If you tell me how Hostinger lets you set env vars for openclaw-zs4q-openclaw-1 (panel screenshot or description), Iβll tell you exactly what to add and where.
root@srv1100186:~# docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'id; echo "HOME=$HOME"; ls -la "$HOME" || true'
uid=0(root) gid=0(root) groups=0(root)
HOME=/data
total 32
drwxr-xr-x 8 node node 4096 Feb 15 09:23 .
drwxr-xr-x 1 root root 4096 Feb 16 03:07 ..
drwxr-xr-x 8 node node 4096 Feb 15 09:23 .cache
drwx------ 5 node node 4096 Feb 16 05:04 .config
drwxr-xr-x 5 node node 4096 Feb 16 03:11 .npm
drwx------ 13 node node 4096 Feb 16 03:39 .openclaw
drwx------ 3 node node 4096 Feb 15 07:04 .pki
drwxr-xr-x 3 node node 4096 Feb 15 06:16 linuxbrew
root@srv1100186:~# docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'id; echo "HOME=$HOME"'
uid=1000(node) gid=1000(node) groups=1000(node)
HOME=/data
root@srv1100186:~# uname -m
x86_64
root@srv1100186:~#
Cool β youβre on x86_64, and you can exec as the node user with HOME=/data. Thatβs basically the best-case for this.
Below is a setup that makes gog + its tokens persist in /data (so container restarts donβt wipe it).
1) Install gog into /data/bin (persistent)
Option A (recommended): download inside the container as node
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc '
set -eu
mkdir -p /data/bin
cd /tmp
VER=0.11.0
# use curl if available, otherwise wget
if command -v curl >/dev/null; then
curl -fsSL -o gog.tgz "https://github.com/steipete/gogcli/releases/download/v${VER}/gogcli_${VER}_linux_amd64.tar.gz"
else
wget -O gog.tgz "https://github.com/steipete/gogcli/releases/download/v${VER}/gogcli_${VER}_linux_amd64.tar.gz"
fi
tar -xzf gog.tgz gog
mv gog /data/bin/gog
chmod +x /data/bin/gog
/data/bin/gog --version
'
Option B: download on the host, then copy in
(Use this if TLS/download tools are missing in-container.)
VER=0.11.0
wget "https://github.com/steipete/gogcli/releases/download/v${VER}/gogcli_${VER}_linux_amd64.tar.gz"
tar -xzf "gogcli_${VER}_linux_amd64.tar.gz" gog
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'mkdir -p /data/bin'
docker cp ./gog openclaw-zs4q-openclaw-1:/data/bin/gog
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'chown node:node /data/bin/gog && chmod +x /data/bin/gog'
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc '/data/bin/gog --version'
2) Put your OAuth client JSON into /data
On your server, copy your downloaded Google βDesktop appβ OAuth client JSON as client_secret.json, then:
docker cp ./client_secret.json openclaw-zs4q-openclaw-1:/data/client_secret.json
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'chown node:node /data/client_secret.json'
3) Configure keyring + authenticate (headless/manual)
Open a shell as node:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh
Then run (choose a password and keep it safe):
export GOG_KEYRING_BACKEND=file
export GOG_KEYRING_PASSWORD='put-a-long-random-password-here'
/data/bin/gog auth keyring file
/data/bin/gog auth credentials /data/client_secret.json
/data/bin/gog auth add you@yourdomain.com --services gmail,calendar,drive,contacts,sheets,docs --manual
It will print an auth URL β open it locally β approve β copy the full redirect URL from your browser address bar β paste it back.
root@srv1100186:~# docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'id; echo "HOME=$HOME"'
uid=1000(node) gid=1000(node) groups=1000(node)
HOME=/data
root@srv1100186:~# uname -m
x86_64
root@srv1100186:~# docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc '
set -eu
mkdir -p /data/bin
cd /tmp
VER=0.11.0
use curl if available, otherwise wget
if command -v curl >/dev/null; then
curl -fsSL -o gog.tgz "https://github.com/steipete/gogcli/releases/download/v${VER}/gogcli_${VER}_linux_amd64.tar.gz"
4) Test
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc '
export GOG_KEYRING_BACKEND=file
export GOG_KEYRING_PASSWORD="(same password)"
/data/bin/gog auth list --check
/data/bin/gog gmail labels list --max 5
'
5) Make OpenClaw able to call gog automatically
Youβll need these env vars set on the container (Hostinger panel / whatever manages it), so it works non-interactively:
GOG_KEYRING_BACKEND=fileGOG_KEYRING_PASSWORD=...- (optional)
GOG_ACCOUNT=you@yourdomain.com - (optional) add
/data/bintoPATHor symlinkgoginto/usr/local/bin
x86_64
root@srv1100186:~# docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc '
set -eu
mkdir -p /data/bin
cd /tmp
VER=0.11.0
use curl if available, otherwise wget
if command -v curl >/dev/null; then
curl -fsSL -o gog.tgz "https://github.com/steipete/gogcli/releases/download/v${VER}/gogcli_${VER}_linux_amd64.tar.gz"
^[[200~docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc '
export GOG_KEYRING_BACKEND=file
export GOG_KEYRING_PASSWORD="(same password)"
/data/bin/gog auth list --check
/data/bin/gog gmail labels list --max 5
'^[[201~sh: 9: Syntax error: end of file unexpected (expecting "fi")
root@srv1100186:~#
If you tell me where you set env vars for openclaw-zs4q-openclaw-1 (Hostinger UI path / screenshot), Iβll tell you exactly what to enter so OpenClaw can use gog without you manually exporting anything.
already set and deployed the env. variable
Your paste/log cuts off mid-script right after the curl ... gog.tgz line, so you probably just need to finish the rest (or re-run as a complete block).
Run this full install block (copy/paste as-is):
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc '
set -eu
mkdir -p /data/bin
cd /tmp
VER=0.11.0
if command -v curl >/dev/null 2>&1; then
curl -fsSL -o gog.tgz "https://github.com/steipete/gogcli/releases/download/v${VER}/gogcli_${VER}_linux_amd64.tar.gz"
else
wget -O gog.tgz "https://github.com/steipete/gogcli/releases/download/v${VER}/gogcli_${VER}_linux_amd64.tar.gz"
fi
tar -xzf gog.tgz gog
mv gog /data/bin/gog
chmod +x /data/bin/gog
/data/bin/gog --version
'
If you get tar: not found, do the βdownload on host + docker cpβ method instead.
Verify it installed
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'ls -l /data/bin/gog && /data/bin/gog --version'
Once thatβs good, next step is OAuth:
You accidentally pasted a second docker exec ... command inside the first quoted script, so /bin/sh never saw the closing fi β expecting "fi".
Run the install as a single command (no if/fi, so itβs hard to break):
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc '
set -eu
mkdir -p /data/bin
cd /tmp
VER=0.11.0
URL="https://github.com/steipete/gogcli/releases/download/v${VER}/gogcli_${VER}_linux_amd64.tar.gz"
if command -v curl >/dev/null 2>&1; then
curl -fsSL -o gog.tgz "$URL"
else
wget -O gog.tgz "$URL"
- Copy your OAuth client json into
/data:
docker cp ./client_secret.json openclaw-zs4q-openclaw-1:/data/client_secret.json
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'chown node:node /data/client_secret.json'
- Run the manual auth flow:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc '
export GOG_KEYRING_BACKEND=file
export GOG_KEYRING_PASSWORD="(set this to a strong password)"
/data/bin/gog auth keyring file
/data/bin/gog auth credentials /data/client_secret.json
/data/bin/gog auth add you@yourdomain.com --services gmail,calendar,drive,contacts,sheets,docs --manual
'
fi
tar -xzf gog.tgz gog
mv gog /data/bin/gog
chmod +x /data/bin/gog
/data/bin/gog --version
'
Then verify (separate command):
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'ls -l /data/bin/gog && /data/bin/gog --version'
Only after that run the auth/test commands (also separately).
Good β if you already set the env vars on the container, the next step is just to verify theyβre actually visible inside the running container and that gog is callable.
1) Verify env + binary (safe to paste output)
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc '
echo "HOME=$HOME"
echo "PATH=$PATH"
env | grep -E "^GOG_" | sed "s/=.*/=***REDACTED***/"
ls -l /data/bin/gog || true
/data/bin/gog --version
command -v gog || true
'
- If
command -v gogprints nothing, thatβs fine β you can still use/data/bin/gog(or add/data/bintoPATHin your container env).
2) If you havenβt done OAuth yet (headless/manual)
Paste whatever error you hit (especially from the install block), and Iβll guide the next fix.
If the install still fails, paste the full output from that install command (from the docker exec ... line to the end).
(Do not paste your password/token output here.)
First ensure your OAuth client JSON is in the container (example path):
/data/client_secret.json
Then:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc '
/data/bin/gog auth keyring
/data/bin/gog auth credentials /data/client_secret.json
/data/bin/gog auth add you@yourdomain.com --services gmail,calendar,drive,contacts,sheets,docs --manual
'
3) Test auth
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc '
/data/bin/gog auth list --check
/data/bin/gog gmail labels list --max 5
'
If any of these fail, paste the error text only (and redact anything that looks like a token/URL with code= / state= / passwords).
I've added 8 redirect URIs to my credentials file. Now add ALL of these to Google Cloud Console β Credentials β OAuth Client β Redirect URIs:
http://127.0.0.1:38693/oauth2/callback
http://127.0.0.1:43995/oauth2/callback
http://127.0.0.1:36641/oauth2/callback
http://127.0.0.1:35117/oauth2/callback
http://127.0.0.1:36445/oauth2/callback
http://127.0.0.1:46803/oauth2/callback
http://127.0.0.1:45195/oauth2/callback
Once all 7 are added, let me know and I'll run the auth again - it should work with one of these ports!
That message is a big hint youβre using the wrong OAuth client type in Google Cloud.
Whatβs happening
gog uses a loopback redirect like:
http://127.0.0.1:<random-port>/oauth2/callback
The port is ephemeral, so trying to pre-register a bunch of ports (or editing the JSON) is a losing game if the OAuth client is a βWeb applicationβ client, because web clients require exact redirect URIs.
Fix (recommended)
Create a new OAuth client of type Desktop app:
- Google Cloud Console β APIs & Services β Credentials
- Create Credentials β OAuth client ID
- Application type: Desktop app
- Download the JSON (donβt hand-edit it)
- Copy it into the container (example path):
docker cp ./client_secret.json openclaw-zs4q-openclaw-1:/data/client_secret.json
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'chown node:node /data/client_secret.json'
Then re-store credentials + re-auth:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc '
/data/bin/gog auth credentials /data/client_secret.json
/data/bin/gog auth add you@yourdomain.com --services gmail,calendar,drive,contacts,sheets,docs --manual
'
Also check (common gotcha)
If your OAuth consent screen is in Testing, make sure your Google account is added under Test users, or auth will fail in weird ways.
If it still errors, paste just the gog auth add ... --manual error output (redact any code= / state= parts of URLs).
-rwxr-xr-x 1 node node 22819000 Feb 16 05:50 /data/bin/gog
v0.11.0 (91c4c15 2026-02-15T03:29:18Z)
root@srv1100186:~# docker cp ./client_secret.json openclaw-zs4q-openclaw-1:/data/client_secret.json
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'chown node:node /data/client_secret.json'
lstat /root/client_secret.json: no such file or directory
chown: cannot access '/data/client_secret.json': No such file or directory
root@srv1100186:~#
gog is installed fine. The failure is just: the OAuth JSON file isnβt on the server where you ran docker cp.
docker cp ./client_secret.json ... looks for the file on the host in your current directory (here: /root), and you donβt have it there:
lstat /root/client_secret.json: no such file or directory
1) Locate the file on the server (maybe it has the long default name)
Run on the server:
ls -la
ls -la /root
find /root -maxdepth 2 -name 'client_secret*.json' -o -name '*apps.googleusercontent.com*.json'
2) If the file is only on your laptop, upload it to the server
From your laptop (replace the path/name as needed):
scp ~/Downloads/client_secret_*.json root@srv1100186:/root/client_secret.json
(Use your server IP/hostname instead of srv1100186 if thatβs not resolvable from your machine.)
3) Copy it into the container + fix ownership
Back on the server:
docker cp /root/client_secret.json openclaw-zs4q-openclaw-1:/data/client_secret.json
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'chown node:node /data/client_secret.json && ls -l /data/client_secret.json'
4) Continue gog auth inside the container
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc '
/data/bin/gog auth credentials /data/client_secret.json
/data/bin/gog auth add you@yourdomain.com --services gmail,calendar,drive,contacts,sheets,docs --manual
'
If you paste the output of ls -la + the find ...client_secret... command, I can tell you the exact filename/path you already have (if itβs there).
[build]:
2026-02-16T05:14:33.420694 Pulling project images
[build]:
2026-02-16T05:14:33.421266 ------------------------------
[build]:
2026-02-16T05:14:33.460353 invalid containerPort: N4jyUX!kcTr1wsnFMSde
[build]:
2026-02-16T05:14:33.462502 ------------------------------
[build]:
2026-02-16T05:14:33.462548 Image pull failed with code 1
[build]:
2026-02-16T05:14:33.462683 Pull failed, will retry again after 30 seconds... (1/3)
[build]:
2026-02-16T05:15:03.463879 ------------------------------
[build]:
2026-02-16T05:15:03.534494 invalid containerPort: N4jyUX!kcTr1wsnFMSde
[build]:
2026-02-16T05:15:03.596095 ------------------------------
[build]:
2026-02-16T05:15:03.596145 Image pull failed with code 1
[build]:
2026-02-16T05:15:03.596261 Pull failed, will retry again after 60 seconds... (2/3)
[build]:
2026-02-16T05:16:03.597557 ------------------------------
[build]:
2026-02-16T05:16:03.635417 invalid containerPort: N4jyUX!kcTr1wsnFMSde
[build]:
2026-02-16T05:16:03.689654 ------------------------------
[build]:
2026-02-16T05:16:03.689698 Image pull failed with code 1
[build]:
2026-02-16T05:16:03.689821 Pull failed, will retry again after 90 seconds... (3/3)
That log is not a gog error β itβs Hostingerβs Docker Manager/template failing before it even pulls the image:
invalid containerPort: ...
containerPort must be a number (e.g. 18789), but itβs currently set to a string that looks like a secret/password.
What to do
-
Rotate that secret (since it got posted into the thread).
- If that value was your
OPENCLAW_GATEWAY_TOKENorGOG_KEYRING_PASSWORD, change it in the Hostinger environment panel and restart/redeploy.
- If that value was your
-
In Hostinger hPanel β Docker Manager β your OpenClaw project β Settings/Ports:
- Find the field named Container Port /
containerPort - Set it back to a numeric port (for OpenClaw itβs typically 18789 in these templates)
- Make sure your secret is in the Environment variables section, not in Ports.
- Find the field named Container Port /
-
On the server, double-check what port the running container exposes (if itβs still running):
docker ps --format 'table {{.Names}}\t{{.Ports}}\t{{.Status}}'
docker port openclaw-zs4q-openclaw-1 || true
docker inspect openclaw-zs4q-openclaw-1 --format '{{json .Config.ExposedPorts}}'
Paste the output of the docker ps ...Ports... line and Iβll tell you exactly what the container port should be set to in Hostingerβs UI.
how to fix and run doctor
That invalid containerPort: N4jyUX!kcTr1wsnFMSde error means your Hostinger Docker Manager config has a secret string in the βcontainerPortβ field. containerPort must be a number.
Fix the deploy (Hostinger Docker Manager)
In hPanel β Docker Manager β your OpenClaw project/template:
- Go to Ports (or βExposed portsβ / βNetworkingβ)
- Set:
- containerPort =
18789(numeric) - public/host port = whatever you want (Hostinger often assigns one; it must be numeric too)
- Make sure your gateway token is in Environment variables (e.g.
OPENCLAW_GATEWAY_TOKEN=...), not in Ports.
Also: rotate that secret
Since that value got pasted into chat/logs, treat it as compromised and change it in the Hostinger env-var UI.
Run βdoctorβ (inside the running container)
First check the CLI name:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'command -v openclaw && openclaw --version'
Then run:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'openclaw doctor'
If openclaw isnβt found, run:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'ls -la /usr/local/bin | grep -E "openclaw|clawdbot" || true'
Paste:
- the Ports section screenshot/text (just the port numbers, redact secrets)
- output of
openclaw --versionandopenclaw doctor
and Iβll tell you exactly what to change.
the gateway opens through tailnet in my browser but it keeps showing loading chat
That βControl UI loads but chat stays on Loadingβ¦β is almost always because youβre opening it over plain HTTP on a Tailnet/LAN IP (e.g. http://100.x.y.z:18789). In that case the browser is in a non-secure context, WebCrypto gets blocked, and OpenClawβs Control UI canβt complete the device-identity + WS handshake properly.
Fix (recommended): use HTTPS via Tailscale Serve
OpenClaw has a built-in βServeβ mode that keeps the gateway bound to loopback and lets Tailscale provide HTTPS:
Docs: https://docs.openclaw.ai/web/control-ui#tailnet-access-recommended
Tailscale mode docs: https://docs.openclaw.ai/gateway/tailscale
In your container, first see what mode/auth youβre in:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'openclaw --version && openclaw gateway status'
If you can run the gateway with Serve enabled, the CLI form is:
openclaw gateway --tailscale serve
Then you open the https://β¦ URL (MagicDNS / ts.net) instead of http://100.xβ¦.
Alternative quick workaround (less secure): allow insecure HTTP
If you must use http://<tailscale-ip>, you can downgrade the Control UI auth to allow insecure contexts:
{
gateway: {
controlUi: { allowInsecureAuth: true },
bind: "tailnet",
auth: { mode: "token", token: "..." }
}
}
(Thatβs from the Control UI docs section βInsecure HTTPβ.)
Also check: device pairing required
If your browser/device is new, you may need to approve it:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'openclaw devices list'
If you see a pending request, approve it:
openclaw devices approve <requestId>
If you paste the output of openclaw gateway status (redact tokens), I can tell you which of the above applies immediately (HTTP secure-context vs pairing vs auth mismatch).
] Telegram: ok (@heisenbergclawbot) (57ms)
Agents: main (default)
Heartbeat interval: 1h (main)
Session store (main): /data/.openclaw/agents/main/sessions/sessions.json (1 entries)
- agent:main:main (4m ago)
Run "openclaw doctor --fix" to apply changes.
β
β Doctor complete.
root@srv1100186:~# docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'openclaw --version && openclaw gateway status'
2026.2.12
π¦ OpenClaw 2026.2.12 (f9e444d) β Because texting yourself reminders is so 2024.
β
β
Service: systemd (disabled)
File logs: /tmp/openclaw/openclaw-2026-02-16.log
Service config looks out of date or non-standard.
Service config issue: Gateway service PATH is not set; the daemon should use a minimal PATH.
Recommendation: run "openclaw doctor" (or "openclaw doctor --repair").
Config (cli): ~/.openclaw/openclaw.json
Config (service): ~/.openclaw/openclaw.json
Gateway: bind=loopback (127.0.0.1), port=18789 (env/config)
Probe target: ws://127.0.0.1:18789
Dashboard: http://127.0.0.1:18789/
Probe note: Loopback-only gateway; only local clients can connect.
Runtime: unknown (Error: systemctl --user unavailable: Failed to connect to user scope bus via local transport: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not defined (consider using --machine=<user>@.host --user to connect to bus of other user))
RPC probe: ok
systemd user services unavailable.
systemd user services are unavailable; install/enable systemd or run the gateway under your supervisor.
If you're in a container, run the gateway in the foreground instead of openclaw gateway.
Troubles: run openclaw status
Troubleshooting: https://docs.openclaw.ai/troubleshootin
?
Your gateway status is the key clue:
- Gateway: bind=loopback (127.0.0.1), port=18789
- βLoopback-only gateway; only local clients can connect.β
So if you open the UI over Tailnet using plain http://<tailscale-ip>:18789, youβll often get exactly what you describe: UI shell loads, but chat sits on βLoadingβ¦β because the WebSocket/auth/device-identity handshake canβt complete (and if itβs plain HTTP, the browser is also in a non-secure context which breaks WebCrypto/device identity).
Fix it (pick one)
Option A (recommended): Use Tailscale Serve (HTTPS) for the Control UI
Docs: https://docs.openclaw.ai/web/control-ui#tailnet-access-recommended
On the VPS (host, not your laptop), run Tailscale Serve to proxy the loopback gateway with HTTPS.
If you have the tailscale CLI on the VPS:
tailscale serve https / http://127.0.0.1:18789
Then open the https://β¦ URL that Tailscale Serve gives you (MagicDNS / ts.net). This usually makes βLoading chatβ immediately go away.
If OpenClaw is managing tailscale for you, the equivalent is:
openclaw gateway --tailscale serve
(only works if tailscale is installed/logged in where the gateway runs)
Option B (works, less secure): Allow insecure HTTP auth
Docs: https://docs.openclaw.ai/web/control-ui#insecure-http
Set:
gateway.controlUi.allowInsecureAuth: true
(Then restart the gateway/container.) This is a workaround when you insist on http://... instead of HTTPS.
Also check: device pairing (can look like βloadingβ)
Run:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'openclaw devices list'
If thereβs a pending request, approve it:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'openclaw devices approve <requestId>'
Two quick questions so I can tell you the exact next command
- What exact URL are you opening (http vs https, and hostname)?
- On the VPS, do you have
tailscaleinstalled on the host? (tailscale status)
(Also: youβre on OpenClaw 2026.2.12 β Iβd strongly recommend updating to β₯ 2026.2.13 when you get a chance; 2026.2.12 had some nasty edge-case bugs.)
Troubles: run openclaw status
Troubleshooting: https://docs.openclaw.ai/troubleshooting
root@srv1100186:~# bash
root@srv1100186:~# tailscale serve https / http://127.0.0.1:18789
Error: the CLI for serve and funnel has changed. You can run the following command instead:
- tailscale serve --bg http://127.0.0.1:18789
Please see https://tailscale.com/kb/1242/tailscale-serve for more information.
try tailscale serve --help for usage info
root@srv1100186:~#
βLoading chatβ = the Control UI HTML loaded, but the WebSocket connection to the gateway never fully comes up (auth / secure-context / pairing).
From your openclaw gateway status youβre on bind=loopback (127.0.0.1), so the correct way to access it remotely over Tailnet is HTTPS via Tailscale Serve (plain http://<tailscale-ip>:β¦ often gets stuck).
1) Find what port the host maps to the container
Run on the VPS:
docker port openclaw-zs4q-openclaw-1
Youβll get something like 18789/tcp -> 0.0.0.0:XXXXX. That XXXXX is the host port.
2) Put that behind Tailscale Serve (HTTPS)
On the VPS (host):
tailscale serve https / http://127.0.0.1:XXXXX
tailscale serve status
Now open the https://β¦ts.net/ URL that tailscale serve status shows.
3) If it still βloads foreverβ: approve the browser device (pairing)
Inside the container:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'openclaw devices list'
If thereβs a pending request:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'openclaw devices approve <requestId>'
Paste the output of:
docker port openclaw-zs4q-openclaw-1- and tell me the exact URL youβre opening (http vs https)
and Iβll tell you the exacttailscale servecommand + what you should see next.
Yeah, that error is normal on newer Tailscale (>= 1.52) β the tailscale serve https / ... syntax changed.
Use this instead (HTTPS is the default):
tailscale serve --bg --set-path=/ http://127.0.0.1:18789
tailscale serve status
Then open the https:// URL Tailscale prints (usually something like):
https://<your-node-name>.<tailnet>.ts.net/
That should fix the βControl UI loads but chat stays on Loadingβ¦β issue (because youβre no longer using plain http://<tailscale-ip>).
If the UI then says pairing is required / still doesnβt connect, approve the browser device:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'openclaw devices list'
# then:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'openclaw devices approve <requestId>'
To undo Serve later:
tailscale serve reset
root@srv1100186:~# docker port openclaw-zs4q-openclaw-1
43710/tcp -> 127.0.0.1:43710
root@srv1100186:~#
Perfect β that means the host is exposing OpenClaw on 127.0.0.1:43710 (not 18789). So Tailscale Serve needs to proxy that port.
1) Serve the published port over Tailnet HTTPS
On the VPS (host shell):
tailscale serve reset
tailscale serve --bg --set-path=/ http://127.0.0.1:43710
tailscale serve status
Then open the https://β¦ts.net/ URL shown by tailscale serve status.
2) If it still says βLoading chatβ¦β, approve the Control UI device
Inside the container:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'openclaw devices list'
If you see a pending requestId, approve it:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'openclaw devices approve <requestId>'
If you paste the output of tailscale serve status (redact nothing sensitive; itβs usually safe) and openclaw devices list, I can tell you exactly what itβs waiting on.
the ui opens but nothing is clickable inside the ui
2026-02-16T05:19:38.012Z [gateway] log file: /tmp/openclaw/openclaw-2026-02-16.log
[Mon Feb 16 2026 12:19:38.014]
openclaw-1:
2026-02-16T05:19:38.014Z [browser/service] Browser control service ready (profiles=2)
[Mon Feb 16 2026 12:19:38.039]
openclaw-1:
2026-02-16T05:19:38.039Z [telegram] [default] starting provider (@heisenbergclawbot)
[Mon Feb 16 2026 12:19:38.127]
openclaw-1:
2026-02-16T05:19:38.127Z [gateway] update available (latest): v2026.2.15 (current v2026.2.12). Run: openclaw update
[Mon Feb 16 2026 12:19:39.911]
openclaw-1:
2026-02-16T05:19:39.911Z [ws] Proxy headers detected from untrusted address. Connection will not be treated as local. Configure gateway.trustedProxies to restore local client detection behind your proxy.
[Mon Feb 16 2026 12:19:44.378]
openclaw-1:
2026-02-16T05:19:44.377Z [diagnostic] lane wait exceeded: lane=session:agent:main:main waitedMs=5584 queueAhead=0
[Mon Feb 16 2026 12:19:52.907]
openclaw-1:
2026-02-16T06:19:52.906+01:00 Still "test". Please send me your Maton API key directly - I'll add it to the config.
[Mon Feb 16 2026 12:25:55.173]
openclaw-1:
2026-02-16T05:25:55.173Z [ws] Proxy headers detected from untrusted address. Connection will not be treated as local. Configure gateway.trustedProxies to restore local client detection behind your proxy.
[Mon Feb 16 2026 12:25:55.408]
openclaw-1:
2026-02-16T05:25:55.408Z [ws] webchat connected conn=33430d4e-017f-409f-ba3a-8f8fa447c9d0 remote=127.0.0.1 client=openclaw-control-ui webchat vdev
[Mon Feb 16 2026 12:26:44.643]
openclaw-1:
2026-02-16T05:26:44.643Z [ws] webchat disconnected code=1005 reason=n/a conn=33430d4e-017f-409f-ba3a-8f8fa447c9d0
[Mon Feb 16 2026 12:27:53.812]
openclaw-1:
2026-02-16T05:27:53.812Z [ws] β res β channels.status 50ms conn=3c3ac94cβ¦bd
If the Control UI renders but nothing is clickable, that usually means the JS bundle isnβt running (so the page is basically βdead HTMLβ), or the UI is stuck behind a βdisconnectedβ overlay because the WebSocket canβt stay connected.
Given your docker port:
- host is exposing: http://127.0.0.1:43710/
- so Tailscale Serve must proxy 43710 (not 18789)
1) Make sure Tailscale Serve is proxying the right port
On the VPS host:
tailscale serve reset
tailscale serve --bg --set-path=/ http://127.0.0.1:43710
tailscale serve status
Open the https://β¦ts.net/ URL it shows.
2) Verify the UI JS bundle is actually loading (this is the βunclickable UIβ smoking gun)
On the VPS host:
BASE=http://127.0.0.1:43710
curl -fsSL "$BASE/" | head
JS=$(curl -fsSL "$BASE/" | grep -oE '/assets/[^"]+\\.js' | head -1)
echo "JS=$JS"
curl -I "$BASE$JS"
What you want:
- HTML fetch works
- JS
curl -Ireturns 200 and a JS-ish content-type (not HTML)
If that JS request is 404 or returns text/html, the UI will look loaded but be totally non-interactive.
3) Check if device pairing is blocking the session
Inside the container:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'openclaw devices list'
If thereβs a pending request, approve it:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'openclaw devices approve <requestId>'
4) Youβre on 2026.2.12 (and logs say 2026.2.15 exists)
If the above looks correct but UI is still βdeadβ, updating is very likely to fix it. If you want, tell me βok updateβ and Iβll give you the safest update steps for the Hostinger container setup.
Paste the output of:
tailscale serve status- the
curl -I "$BASE$JS"result openclaw devices list(it shouldnβt contain secrets)
β¦and weβll pinpoint which branch it is.
root@srv1100186:~# tailscale serve reset
tailscale serve --bg --set-path=/ http://127.0.0.1:43710
tailscale serve status
Available within your tailnet:
https://srv1100186.tail777ba9.ts.net/
|-- proxy http://127.0.0.1:43710
Serve started and running in the background.
To disable the proxy, run: tailscale serve --https=443 off
https://srv1100186.tail777ba9.ts.net (tailnet only)
|-- / proxy http://127.0.0.1:43710
root@srv1100186:~# BASE=http://127.0.0.1:43710
curl -fsSL "$BASE/" | head
JS=$(curl -fsSL "$BASE/" | grep -oE '/assets/[^"]+\.js' | head -1)
echo "JS=$JS"
curl -I "$BASE$JS"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>OpenClaw</title>
<link rel="icon" type="image/png" href="/assets/openclaw.png">
<style>
* {
margin: 0;
padding: 0;
JS=
HTTP/1.1 200 OK
X-Powered-By: Express
Cache-Control: no-store, no-cache, must-revalidate
Content-Type: text/html; charset=utf-8
Content-Length: 14436
ETag: W/"3864-0ixzwU6AhawL+KLrvjY1oLpfWvU"
Date: Mon, 16 Feb 2026 06:08:22 GMT
Connection: keep-alive
Keep-Alive: timeout=5
root@srv1100186:~#
Your curl output is the smoking gun:
- The HTML loads fine
- But
JS=came back empty β the page isnβt referencing any/assets/*.jsbundle - And your
curl -I "$BASE$JS"returnedContent-Type: text/html(becauseJSwas empty and you just fetched/again)
If the Control UI JS bundle never loads, the UI will look βthereβ but be totally dead / not clickable.
Confirm in 1 command
On the VPS:
BASE=http://127.0.0.1:43710
curl -fsSL "$BASE/" | grep -iE '<script|\.js' | head -50
- If that prints nothing (or no
src=...js), your image is serving an HTML shell without the built UI JS.
Fix
Youβre on OpenClaw 2026.2.12 and your logs literally say thereβs an update available (2026.2.15). This kind of βUI shell but no assets / broken UIβ is exactly the sort of thing that gets fixed by updating the container/image.
Since youβre running ghcr.io/hostinger/hvps-openclaw:latest, the practical fix is:
- Update/redeploy the container in Hostinger Docker Manager (pull latest image + restart)
After the update, rerun:
BASE=http://127.0.0.1:43710
curl -fsSL "$BASE/" | grep -oE '/assets/[^"'"'"' ]+\.js' | head -5
You should now see something like /assets/index-<hash>.js.
Optional (but youβll want it later)
This log line:
Proxy headers detected from untrusted addressβ¦ Configure gateway.trustedProxiesβ¦
is about Tailscale Serve headers/trust; it can affect βlocal client detectionβ and tailnet identity auth, but itβs not what makes the UI completely unclickable. First get the JS bundle loading.
If you tell me how you update this container on Hostinger (button name / screen), I can tell you exactly what to click / what to expect after restart.
root@srv1100186:~# BASE=http://127.0.0.1:43710
curl -fsSL "$BASE/" | grep -iE '<script|.js' | head -50
root@srv1100186:~#
That curl result explains it perfectly: the page youβre serving has zero <script> tags, so the Control UIβs JavaScript bundle never loads. With no JS, the UI will render but be totally dead / unclickable.
So this isnβt a Tailscale issue anymore β itβs a broken Control UI build in what youβre currently running (and youβre on OpenClaw 2026.2.12, while your logs say 2026.2.15 is available).
Fix: update OpenClaw in the container, then restart
Run:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'openclaw update'
docker restart openclaw-zs4q-openclaw-1
Then re-check that the UI now references a JS bundle:
BASE=http://127.0.0.1:43710
curl -fsSL "$BASE/" | grep -iE '<script|\.js' | head -50
You should see something like:
<script type="module" src="/assets/index-XXXX.js"></script>
If openclaw update fails
Paste the output of:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'openclaw update status'
β¦and Iβll tell you the right update path for that Hostinger image (sometimes you need to update via Hostinger Docker Manager instead of inside the container).
(Separate note: your logs show βProxy headers detected from untrusted address β¦ trustedProxiesβ. That can affect βlocalβ detection, but it wouldnβt remove <script> tags β the missing JS bundle is the core reason for the unclickable UI.)
root@srv1100186:~# docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'openclaw update'
docker restart openclaw-zs4q-openclaw-1
Updating OpenClaw...
Update Result: SKIPPED
Root: /usr/local/lib/node_modules/openclaw
Reason: not-git-install
Before: 2026.2.12
Total time: 175ms
Skipped: this OpenClaw install isn't a git checkout, and the package manager couldn't be detected. Update via your package manager, then run openclaw doctor and openclaw gateway restart.
Examples: npm i -g openclaw@latest or pnpm add -g openclaw@latest
openclaw-zs4q-openclaw-1
root@srv1100186:~#
Youβre not actually serving the real OpenClaw Control UI right now.
- Inside the container, OpenClaw says the gateway is on 127.0.0.1:18789.
- But Docker is exposing container port 43710 to the host (
docker port β¦ -> 127.0.0.1:43710). - The HTML you fetch on
:43710has no<script>tags, so it canβt be the real UI (itβs basically a dead placeholder page), hence βnothing clickableβ.
1) Prove the real UI exists on 18789 (inside the container)
Run:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc '
curl -fsSL http://127.0.0.1:18789/ | grep -iE "<script|\\.js" | head -20
'
If you see script tags / /assets/...js there β OpenClaw UI is fine, itβs just not the port youβre exposing.
2) Fix the port mapping in Hostinger Docker Manager
In hPanel β Docker Manager β your OpenClaw project β Ports:
Set it so you expose the gateway port 18789 from the container.
What you want to end up with is something like:
- containerPort: 18789
- host/public port: whatever Hostinger gives you (can stay 43710)
After redeploy/restart, this command should show the correct mapping:
docker port openclaw-zs4q-openclaw-1
Ideally youβll see 18789/tcp -> 127.0.0.1:43710 (host port can differ).
3) Point Tailscale Serve at the (fixed) host port
If host port stays 43710:
tailscale serve reset
tailscale serve --bg --set-path=/ http://127.0.0.1:43710
tailscale serve status
4) If UI loads but says pairing required / still stuck
Approve the browser device:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'openclaw devices list'
# then
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'openclaw devices approve <requestId>'
Paste the output of step (1) (the grep result) + your updated docker port ... after you change the Ports config, and Iβll tell you exactly if itβs fixed.
openclaw-zs4q-openclaw-1
root@srv1100186:~# docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc '
curl -fsSL http://127.0.0.1:18789/ | grep -iE "<script|\.js" | head -20
'
<script type="module" crossorigin src="./assets/index-B4LPvte9.js"></script>
<script>window.OPENCLAW_CONTROL_UI_BASE_PATH="";window.OPENCLAW_ASSISTANT_NAME="Assistant";window.OPENCLAW_ASSISTANT_AVATAR="A";</script></head>
root@srv1100186:~#
Nice β that confirms the real Control UI (with the JS bundle) is being served on 127.0.0.1:18789 inside the container:
<script type="module" ... src="./assets/index-....js"></script>
So the reason your Tailnet URL shows an unclickable UI is simply: youβre currently publishing/proxying the wrong port (43710), which serves a βdeadβ HTML page with no JS.
Fix: change the container port mapping to 18789
Right now:
- host port 43710 is mapped to container port 43710 (wrong)
What you want:
- host port 43710 β container port 18789
You canβt change published ports on an existing container; you must edit ports in Hostinger Docker Manager and redeploy/recreate the container.
In Hostinger hPanel β Docker Manager β your OpenClaw app β Ports:
- Container port:
18789 - Host/Public port: keep
43710(or whatever Hostinger assigns) - Protocol: TCP
After redeploy, verify on the VPS:
docker port openclaw-zs4q-openclaw-1
You should see something like:
18789/tcp -> 127.0.0.1:43710
Then re-point Tailscale Serve to the host port (43710)
tailscale serve reset
tailscale serve --bg --set-path=/ http://127.0.0.1:43710
tailscale serve status
Quick verification (should now show a JS bundle)
curl -fsSL http://127.0.0.1:43710/ | grep -iE '<script|\.js' | head
If it connects but asks for pairing
Remote Control UI connections require device approval:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'openclaw devices list'
# then approve:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'openclaw devices approve <requestId>'
If you paste the docker port ... output after you change the Hostinger port mapping, Iβll confirm itβs correct before you try the UI again.
root@srv1100186:~# docker port openclaw-zs4q-openclaw-1
43710/tcp -> 127.0.0.1:43710
root@srv1100186:~#
Yep β itβs still wrong. Right now Docker says:
- container port 43710 is published to host 43710
But OpenClaw is listening on container port 18789, so you need:
- container port 18789 β host 43710 (or any host port)
You canβt βfixβ that with a command inside the container; the container has to be recreated with the correct port mapping (Hostinger Docker Manager βPortsβ section).
What to change in Hostinger Docker Manager
Find the Ports mapping for this app and set:
- Container port:
18789 - Host/Public port:
43710(or keep whatever Hostinger gives you) - Protocol: TCP
Then redeploy/recreate.
After you change it, verify
Run again:
docker port openclaw-zs4q-openclaw-1
You should see something like:
18789/tcp -> 127.0.0.1:43710
Then Tailscale Serve stays the same
Because Serve proxies the host port:
tailscale serve reset
tailscale serve --bg --set-path=/ http://127.0.0.1:43710
tailscale serve status
If you tell me what Hostinger screen youβre on (or paste the Ports section text), I can tell you exactly which field is currently set to 43710 and what to replace it with.
openclaw-zs4q-openclaw-1
root@srv1100186:~# docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc '
curl -fsSL http://127.0.0.1:18789/ | grep -iE "<script|\.js" | head -20
'
<script type="module" crossorigin src="./assets/index-B4LPvte9.js"></script>
<script>window.OPENCLAW_CONTROL_UI_BASE_PATH="";window.OPENCLAW_ASSISTANT_NAME="Assistant";window.OPENCLAW_ASSISTANT_AVATAR="A";</script></head>
root@srv1100186:~# docker port openclaw-zs4q-openclaw-1
43710/tcp -> 127.0.0.1:43710
root@srv1100186:~# docker port openclaw-zs4q-openclaw-1
43710/tcp -> 127.0.0.1:43710
root@srv1100186:~# bash
root@srv1100186:~# tailscale serve reset
tailscale serve --bg --set-path=/ http://127.0.0.1:43710
tailscale serve status
Available within your tailnet:
https://srv1100186.tail777ba9.ts.net/
|-- proxy http://127.0.0.1:43710
Serve started and running in the background.
To disable the proxy, run: tailscale serve --https=443 off
https://srv1100186.tail777ba9.ts.net (tailnet only)
|-- / proxy http://127.0.0.1:43710
root@srv1100186:~#
and now?
Now you need to fix the Docker port mapping. Tailscale Serve is doing the right thing (proxying host 127.0.0.1:43710), but host 43710 is still wired to the wrong port inside the container.
Right now you have:
- inside container: OpenClaw UI is on 127.0.0.1:18789 β
- host mapping: 43710 -> container 43710 β
- tailscale serve: https://β¦ -> host 43710 β
So the missing piece is: make host 43710 forward to container 18789.
What you must do (Hostinger Docker Manager)
In hPanel β Docker Manager β your OpenClaw deployment β Ports:
Set it to:
- Container port:
18789 - Host/public port:
43710(or any port Hostinger lets you choose) - TCP
Then redeploy/recreate the container (ports canβt be changed in-place).
After redeploy, verify on the VPS
docker port openclaw-zs4q-openclaw-1
You should see:
18789/tcp -> 127.0.0.1:43710
Quick βdid we fix it?β check
curl -fsSL http://127.0.0.1:43710/ | grep -iE '<script|\.js' | head
You should now see the same JS <script β¦ index-β¦.js> line you saw on 18789.
Once thatβs true, open:
https://srv1100186.tail777ba9.ts.net/
If you want, paste a screenshot/text of the Ports section in Hostinger (redact tokens), and Iβll tell you exactly what to change in that UI.
what commands to fix in terminal
You canβt change a Docker containerβs published port mapping in-place. From the terminal, the fix is: recreate the container with -p 127.0.0.1:43710:18789 (host 43710 β container 18789).
Below is a terminal-only βswap containerβ recipe that preserves your /data volume and env vars.
A) Recreate the container with the correct port mapping
1) Capture the current image + env (donβt paste the env output anywhere)
NAME=openclaw-zs4q-openclaw-1
IMG=$(docker inspect -f '{{.Config.Image}}' "$NAME")
echo "IMG=$IMG"
docker inspect -f '{{range .Config.Env}}{{println .}}{{end}}' "$NAME" > /root/openclaw.env
2) Stop + rename the old container (so you can roll back)
docker stop "$NAME"
docker rename "$NAME" "${NAME}-old"
3) Run a new container with the fixed port mapping
docker run -d \
--name "$NAME" \
--restart unless-stopped \
--env-file /root/openclaw.env \
--volumes-from "${NAME}-old" \
-p 127.0.0.1:43710:18789 \
"$IMG"
4) Verify it worked
Now this should show JS <script β¦> (same as your internal 18789 check):
curl -fsSL http://127.0.0.1:43710/ | grep -iE '<script|\.js' | head -5
docker port "$NAME"
5) Keep Tailscale Serve pointing at 43710 (same as you already did)
tailscale serve reset
tailscale serve --bg --set-path=/ http://127.0.0.1:43710
tailscale serve status
Once this is done, your https://srv1100186.tail777ba9.ts.net/ should be clickable.
Rollback (if anything goes wrong)
docker rm -f openclaw-zs4q-openclaw-1
docker rename openclaw-zs4q-openclaw-1-old openclaw-zs4q-openclaw-1
docker start openclaw-zs4q-openclaw-1
If after the port fix the UI loads but wonβt connect, the next check is device pairing:
docker exec -u node -it openclaw-zs4q-openclaw-1 sh -lc 'openclaw devices list'
β οΈ Agent failed before reply: All models failed (2): anthropic/claude-sonnet-4-5: session file locked (timeout 10000ms): pid=28 /data/.openclaw/agents/main/sessions/de64abe5-d8e6-4698-b87d-637c02c79393.jsonl.lock (timeout) | minimax/MiniMax-M2.5: session file locked (timeout 10000ms): pid=28 /data/.openclaw/agents/main/sessions/de64abe5-d8e6-4698-b87d-637c02c79393.jsonl.lock (timeout).
Logs: openclaw logs --follow
That error means OpenClaw canβt write to its session transcript because a lock file is stuck:
β¦/sessions/<session>.jsonl.lock (timeout)β¦ pid=28
Fastest fix (recommended)
Restart the container (this clears stale locks):
docker restart openclaw-zs4q-openclaw-1
If you want to inspect whatβs holding the lock first
- See what PID 28 is:
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'ps -p 28 -o pid,etime,cmd || true'
- List all lock files:
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'ls -la /data/.openclaw/agents/main/sessions/*.lock 2>/dev/null || true'
If itβs still locked after a restart
(Usually means youβve got a stuck gateway process or multiple copies running.)
Run:
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'ps aux | grep -E "openclaw|dist/index.js" | grep -v grep'
Paste the outputs of the three commands above and Iβll tell you the safest next move (kill specific PID vs delete a stale .lock file).
Also: youβre on 2026.2.12 (your logs said 2026.2.15 exists). Once youβre stable again, updating the Hostinger image/redeploying is worth itβ2026.2.12 had a bunch of session edge cases.
root@srv1100186:~# docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'ls -la /data/.openclaw/agents/main/sessions/*.lock 2>/dev/null || true'
-rw-r--r-- 1 node node 58 Feb 16 02:36 /data/.openclaw/agents/main/sessions/d473d003-a5c8-4f78-a47a-3e57bf863ce8.jsonl.lock
-rw-r--r-- 1 node node 58 Feb 16 07:28 /data/.openclaw/agents/main/sessions/de64abe5-d8e6-4698-b87d-637c02c79393.jsonl.lock
root@srv1100186:~#
Those *.jsonl.lock files are exactly why youβre getting βsession file lockedβ. Next step is to check whether theyβre stale (PID no longer exists) or whether you have two gateway processes fighting over the same session.
1) See what PID the lock thinks is holding it
docker exec -it openclaw-zs4q-openclaw-1 sh -lc '
for f in /data/.openclaw/agents/main/sessions/*.lock; do
echo "== $f"
cat "$f" || true
done
'
2) Check if that PID is actually running (example uses pid=28 from your error)
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'ps -p 28 -o pid,etime,cmd || true'
3) Fix it
If PID 28 exists β simplest fix is a container restart:
docker restart openclaw-zs4q-openclaw-1
If PID 28 does NOT exist β the lock is stale. Move the lock files aside (safer than deleting), then restart:
docker exec -it openclaw-zs4q-openclaw-1 sh -lc '
mkdir -p /data/.openclaw/agents/main/sessions/stale-locks
mv /data/.openclaw/agents/main/sessions/*.lock /data/.openclaw/agents/main/sessions/stale-locks/ 2>/dev/null || true
ls -la /data/.openclaw/agents/main/sessions/stale-locks || true
'
docker restart openclaw-zs4q-openclaw-1
4) Confirm itβs gone
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'ls -la /data/.openclaw/agents/main/sessions/*.lock 2>/dev/null || echo "no locks"'
If it comes back immediately after restart, run:
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'ps aux | grep -E "openclaw|dist/index.js" | grep -v grep'
β¦and paste that output (itβll show if you somehow have multiple OpenClaw processes).
fixed, how can I avoid this from happening again?
That lock happens when OpenClaw is writing to a session transcript and doesnβt get a clean shutdown, or when two OpenClaw processes/containers share the same /data/.openclaw session store.
To avoid it:
-
Make sure only ONE OpenClaw container/process uses
/data- Donβt leave an βoldβ OpenClaw container running that shares the same volume.
- Check:
docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}'
-
Avoid hard kills
- Prefer
docker stop/docker restart(graceful) overdocker kill/ forced reboots.
- Prefer
-
Give the container enough RAM (avoid OOM)
- OOMs / crashes are a common reason locks get left behind.
- Update off 2026.2.12
- Youβre on
2026.2.12and your logs said2026.2.15is available. Newer builds tend to reduce these βstale lockβ edge cases. - Since
openclaw updateis skipped in your image (not a git install), youβll need to update by pulling/redeploying the Docker image in Hostinger Docker Manager (or recreate container afterdocker pull ghcr.io/hostinger/hvps-openclaw:latest).
- Youβre on
If it ever happens again, the clean recovery is what you did: restart. If it persists after restart, thatβs the red flag for βmultiple instances running against the same /dataβ.
root@srv1100186:~# docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}'
NAMES IMAGE STATUS
openclaw-zs4q-openclaw-1 ghcr.io/hostinger/hvps-openclaw:latest Up About an hour
root@srv1100186:~#
Good: only one container. That means the lock files were almost certainly caused by an unclean shutdown (crash/OOM/forced kill) or a stuck gateway process inside the container.
To reduce the chance of it happening again:
1) Avoid hard kills / unclean shutdowns
Prefer:
docker restart openclaw-zs4q-openclaw-1
and avoid docker kill / abrupt VPS reboots while itβs busy.
If you ever stop it, give it time:
docker stop -t 30 openclaw-zs4q-openclaw-1
2) Check for OOM (very common cause)
On the VPS host:
dmesg -T | grep -i oom | tail -50
free -h
docker stats --no-stream openclaw-zs4q-openclaw-1
If you see OOM kills, bump the VPS/container memory.
3) Make sure youβre not accidentally running two gateways inside the one container
This can happen if you manually ran openclaw gateway inside the container while itβs already running.
docker exec -it openclaw-zs4q-openclaw-1 sh -lc 'ps aux | grep -E "openclaw|dist/index.js" | grep -v grep'
You should normally see just the one main gateway process.
4) Update off 2026.2.12 (recommended)
Youβre on 2026.2.12; newer releases tend to fix these edge cases. In your Hostinger setup, openclaw update wonβt work (itβs not a git/npm install inside the image), so update by redeploying/pulling the latest ghcr.io/hostinger/hvps-openclaw:latest in Hostinger Docker Manager.
5) Quick βearly warningβ checks
Disk full can also cause weird shutdown behavior:
df -h /data
If you paste the output of the OOM check (dmesg β¦ | grep -i oom) and the process list (ps aux β¦dist/index.js), I can tell you which of the above is the most likely culprit in your case.
[Tue Feb 17 2026 14:44:01.998]
openclaw-1:
2026-02-17T08:44:01.997+01:00 [tools] browser failed: Can't reach the OpenClaw browser control service (timed out after 15000ms). Start (or restart) the OpenClaw gateway (OpenClaw.app menubar, or openclaw gateway) and try again.
[Tue Feb 17 2026 14:44:23.279]
openclaw-1:
2026-02-17T08:44:23.278+01:00 [openclaw] Uncaught exception: TypeError: Cannot read properties of null (reading 'setSession')
[Tue Feb 17 2026 14:44:23.279]
openclaw-1:
at TLSSocket.setSession (node:_tls_wrap:1132:16)
[Tue Feb 17 2026 14:44:23.279]
openclaw-1:
at Object.connect (node:_tls_wrap:1826:13)
[Tue Feb 17 2026 14:44:23.279]
openclaw-1:
at Client.connect (/usr/local/lib/node_modules/openclaw/node_modules/undici/lib/core/connect.js:70:20)
[Tue Feb 17 2026 14:44:23.279]
openclaw-1:
at connect (/usr/local/lib/node_modules/openclaw/node_modules/undici/lib/dispatcher/client.js:452:21)
[Tue Feb 17 2026 14:44:23.279]
openclaw-1:
at _resume (/usr/local/lib/node_modules/openclaw/node_modules/undici/lib/dispatcher/client.js:627:7)
[Tue Feb 17 2026 14:44:23.279]
openclaw-1:
at resume (/usr/local/lib/node_modules/openclaw/node_modules/undici/lib/dispatcher/client.js:561:3)
[Tue Feb 17 2026 14:44:23.279]
openclaw-1:
at Client.<computed> (/usr/local/lib/node_modules/openclaw/node_modules/undici/lib/dispatcher/client.js:285:31)
[Tue Feb 17 2026 14:44:23.279]
openclaw-1:
at TLSSocket.onHttpSocketClose (/usr/local/lib/node_modules/openclaw/node_modules/undici/lib/dispatcher/client-h1.js:942:18)
[Tue Feb 17 2026 14:44:23.279]
openclaw-1:
at TLSSocket.emit (node:events:531:35)
[Tue Feb 17 2026 14:44:23.279]
openclaw-1:
at node:net:346:12
[Tue Feb 17 2026 14:44:23.317]
openclaw-1:
[07:44:23] WARN: OpenClaw exited with code 1
openclaw is not responding
That stacktrace is a hard crash of the Gateway process, which is why you then see:
browser failed: Can't reach the OpenClaw browser control service(it lives behind the gateway)- and then OpenClaw exits with code 1
The interesting part is this:
TypeError: Cannot read properties of null (reading 'setSession')
innode:_tls_wrapviaundici
That strongly points to a Node runtime / undici TLS session bug/mismatch (often happens on βnonβboringβ Node versions).
What Iβd try first (Docker)
Inside the container, paste these outputs:
openclaw --version
node -v
openclaw status --all
openclaw gateway status
Then do a clean restart:
openclaw gateway restart
# or just:
docker restart openclaw-1
If it keeps crashing: pin Node to LTS
Per docs, OpenClaw requires Node >= 22, but for production Iβd stick to LTS (Node 22 or 24) and avoid bleeding-edge Node in containers.
So in your Dockerfile / image, use something like:
node:22-bookworm-slim(or)node:24-bookworm-slim
Rebuild and redeploy.
Docs reference (triage ladder + runtime requirement):
https://docs.openclaw.ai/help/faq (see βFirst 60 secondsβ¦β + βWhat runtime do I needβ)
https://docs.openclaw.ai/gateway/troubleshooting
One more thing (browser tool in Docker)
Even once it doesnβt crash: if youβre expecting the browser tool to work from a headless container, youβll likely want either:
- a paired node (browser runs on your desktop), or
- Chrome extension relay (
profile="chrome")
But first letβs stop the gateway crash.
If you paste openclaw --version, node -v, and the first ~30 lines before the crash in the log, I can tell you if this is the Node-version landmine or something else.
Last login: Mon Feb 16 08:24:29 2026 from 169.254.0.1
root@srv1100186:~# bash
root@srv1100186:~# openclaw --version
node -v
openclaw status --all
openclaw gateway status
openclaw: command not found
Command 'node' not found, but can be installed with:
apt install nodejs
openclaw: command not found
openclaw: command not found
root@srv1100186:~#
Those commands are running on the host (root@srv1100186), and that host currently has no Node and no OpenClaw installed β so we canβt diagnose the crash from there yet.
Youβve got two paths (pick one):
A) If OpenClaw is running in Docker (most likely)
Run the checks inside the container:
docker ps
docker exec -it openclaw-1 bash # or whatever the container name is
openclaw --version
node -v
openclaw status --all
openclaw gateway status
Also helpful for the crash you pasted earlier:
docker logs openclaw-1 --tail 200
B) If you actually want OpenClaw installed on the host
Install Node 22+, then install OpenClaw:
# after Node is installed:
npm i -g openclaw@latest
openclaw --version
openclaw onboard --install-daemon
Linux quick path doc: https://docs.openclaw.ai/platforms/linux
Docker doc (if youβre following the container route): https://docs.openclaw.ai/install/docker
Tell me which setup youβre aiming for (A container, or B host install), and paste docker ps + docker logs ... --tail 200 (if A).
node -v
openclaw status --all
openclaw gateway status
openclaw: command not found
Command 'node' not found, but can be installed with:
apt install nodejs
openclaw: command not found
openclaw: command not found
root@srv1100186:~# docker ps
docker exec -it openclaw-1 bash # or whatever the container name is
openclaw --version
node -v
openclaw status --all
openclaw gateway status
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d82489a7f4c2 ghcr.io/hostinger/hvps-openclaw:latest "/entrypoint.sh nodeβ¦" 13 minutes ago Up 10 minutes 127.0.0.1:43710->43710/tcp openclaw-zs4q-openclaw-1
Error response from daemon: No such container: openclaw-1
openclaw: command not found
Command 'node' not found, but can be installed with:
apt install nodejs
openclaw: command not found
openclaw: command not found
root@srv1100186:~#
Youβre still running the commands on the host (where thereβs no Node/OpenClaw), because the docker exec ... openclaw-1 failed.
Your container is named:
openclaw-zs4q-openclaw-1
So use that exact name:
docker exec -it openclaw-zs4q-openclaw-1 bash
Then inside the container run:
node -v
openclaw --version || which openclaw || ls -la /usr/local/bin | grep -i openclaw || true
openclaw status --all
openclaw gateway status
And from the host, grab the crash log tail:
docker logs openclaw-zs4q-openclaw-1 --tail 200
Paste:
node -vfrom inside the container- that
docker logs ... --tail 200output
(If openclaw isnβt in PATH in that image, weβll still be able to debug via the logs + the running node ... dist/... command line.)
β
β Node service β systemd not installed β
β Agents β 1 total Β· 1 bootstrapping Β· 1 active Β· 1 sessions β
βββββββββββββββββββ΄ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Channels
ββββββββββββ¬ββββββββββ¬βββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
That output means you are running openclaw status --all inside the container now (good). Two notes:
- βsystemd not installedβ is normal in Docker β ignore it.
- But your paste is truncated right when it gets to the interesting bits (Channels + Gateway status).
1) Please paste these (inside the container)
Run:
node -v
openclaw --version
openclaw gateway status
And from the host, grab the log tail:
docker logs openclaw-zs4q-openclaw-1 --tail 200
Those four outputs will tell us if youβre still hitting the undici / TLSSocket.setSession crash, and what Node version the image uses.
2) About βTailscale inside Dockerβ (recommended pattern: sidecar)
If your goal is: βOpenClaw gateway reachable on my tailnetβ, the clean way is NOT running Tailscale inside the OpenClaw container, but a Tailscale sidecar and put OpenClaw in the same network namespace.
In docker-compose.yml:
services:
tailscale:
image: tailscale/tailscale:stable
hostname: openclaw
cap_add: [ "NET_ADMIN", "NET_RAW" ]
devices: [ "/dev/net/tun:/dev/net/tun" ]
volumes:
- tailscale-state:/var/lib/tailscale
environment:
- TS_AUTHKEY=tskey-...
- TS_STATE_DIR=/var/lib/tailscale
restart: unless-stopped
openclaw:
image: ghcr.io/hostinger/hvps-openclaw:latest
network_mode: service:tailscale
depends_on: [ tailscale ]
restart: unless-stopped
Then you reach the gateway via the Tailscale IP / MagicDNS name of openclaw.
(Also: keep OpenClaw auth/token enabled when exposing anything beyond localhost.)
Once you paste openclaw gateway status + the log tail, Iβll tell you exactly what to change in your setup.
Troubles: run openclaw status
Troubleshooting: https://docs.openclaw.ai/troubleshooting
root@d82489a7f4c2:~# docker logs openclaw-zs4q-openclaw-1 --tail 200
bash: docker: command not found
root@d82489a7f4c2:~# node -v
openclaw --version
openclaw gateway status
v22.22.0
2026.2.12
π¦ OpenClaw 2026.2.12 (f9e444d) β Pairing codes exist because even bots believe in consentβand good security hygiene.
β
β
Service: systemd (disabled)
File logs: /tmp/openclaw/openclaw-2026-02-17.log
Service config looks out of date or non-standard.
Service config issue: Gateway service PATH is not set; the daemon should use a minimal PATH.
Recommendation: run "openclaw doctor" (or "openclaw doctor --repair").
Config (cli): ~/.openclaw/openclaw.json
Config (service): ~/.openclaw/openclaw.json
Gateway: bind=loopback (127.0.0.1), port=18789 (env/config)
Probe target: ws://127.0.0.1:18789
Dashboard: http://127.0.0.1:18789/
Probe note: Loopback-only gateway; only local clients can connect.
Runtime: unknown (Error: systemctl --user unavailable: Failed to connect to user scope bus via local transport: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not defined (consider using --machine=<user>@.host --user to connect to bus of other user))
RPC probe: failed
RPC target: ws://127.0.0.1:18789
gateway closed (1006 abnormal closure (no close frame)): no close reason
Gateway target: ws://127.0.0.1:18789
Source: cli --url
Config: /data/.openclaw/openclaw.json
systemd user services unavailable.
systemd user services are unavailable; install/enable systemd or run the gateway under your supervisor.
If you're in a container, run the gateway in the foreground instead of openclaw gateway.
Troubles: run openclaw status
Troubleshooting: https://docs.openclaw.ai/troubleshooting
root@d82489a7f4c2:~# docker logs openclaw-zs4q-openclaw-1 --tail 200
bash: docker: command not found
root@d82489a7f4c2:~#
Youβre currently inside the container (root@d82489a7f4c2), so docker logs β¦ wonβt work there (docker: command not found). Run docker logs on the host prompt (root@srv1100186:~#).
1) Get the real error/crash (do this inside the container)
These donβt need Docker:
ls -la /tmp/openclaw
tail -n 200 /tmp/openclaw/openclaw-2026-02-17.log
Also check what port is actually listening:
ss -ltnp | egrep ':(18789|43710)\\b' || true
ps auxww | egrep -i 'openclaw|dist/index|node' | head -n 50
2) Grab container stdout logs (do this on the host)
Back on the host:
docker logs openclaw-zs4q-openclaw-1 --tail 200
3) I think youβre probing the wrong port
Your docker ps shows the container exposes 43710, but openclaw gateway status is trying 18789.
Once you confirm which port is listening with ss, try:
openclaw --url ws://127.0.0.1:43710 status --all
# and/or
openclaw --url ws://127.0.0.1:43710 gateway status
4) Version note
Youβre on OpenClaw 2026.2.12. That release had some nasty edge-case bugs; if Hostingerβs :latest image is pinned there, upgrading to an image with 2026.2.13+ is worth doing after we see the log.
Paste:
tail -n 200 /tmp/openclaw/openclaw-2026-02-17.logss -ltnp ...output- host-side
docker logs ... --tail 200
crazy amount of text when I run the first command
root@d82489a7f4c2:~# ss -ltnp | egrep ':(18789|43710)\b' || true
ps auxww | egrep -i 'openclaw|dist/index|node' | head -n 50
bash: ss: command not found
root 1 0.0 0.0 1056 752 ? Ss 08:40 0:00 /sbin/docker-init -- /entrypoint.sh node server.mjs
root 7 0.0 0.0 7408 4092 ? S 08:40 0:00 runuser -u node -- node server.mjs
node 9 0.0 0.9 1787312 79784 ? Sl 08:40 0:00 node server.mjs
root 218 0.0 0.0 3504 1904 pts/0 S+ 09:19 0:00 grep -E -i openclaw|dist/index|node
root@d82489a7f4c2:~#
bash: ss: command not found
root 1 0.0 0.0 1056 752 ? Ss 08:40 0:00 /sbin/docker-init -- /entrypoint.sh node server.mjs
root 7 0.0 0.0 7408 4092 ? S 08:40 0:00 runuser -u node -- node server.mjs
node 9 0.0 0.9 1787312 79784 ? Sl 08:40 0:00 node server.mjs
root 218 0.0 0.0 3504 1904 pts/0 S+ 09:19 0:00 grep -E -i openclaw|dist/index|node
root@d82489a7f4c2:~# openclaw --url ws://127.0.0.1:43710 status --all
and/or
openclaw --url ws://127.0.0.1:43710 gateway status
error: unknown option '--url'
error: unknown option '--url'
root@d82489a7f4c2:~#
That βcrazy amount of textβ is because the log file is JSON-lines (one JSON blob per line). Totally normal β we just need to extract the error lines.
Inside the container: pull only the useful error bits
Run:
LOG="$(ls -t /tmp/openclaw/openclaw-*.log | head -1)"
echo "LOG=$LOG"
# show only likely-failure lines (last ~50 matches)
grep -nE "Uncaught|TypeError|setSession|EADDRINUSE|ERR_|FATAL|crash|exception" "$LOG" | tail -n 50
Also show whether anything is listening:
ss -ltnp | egrep ':(18789|43710)\b' || true
ps auxww | egrep -i 'openclaw|dist/index|entrypoint|node' | head -n 30
Paste those outputs.
On the host (NOT inside the container): get container stdout logs
Exit back to the host shell (root@srv1100186:~#), then:
docker logs openclaw-zs4q-openclaw-1 --tail 200
Paste that too.
One immediate red flag from your openclaw gateway status: itβs probing ws://127.0.0.1:18789 but your Docker mapping shows 43710->43710. The ss -ltnp output will tell us which port the gateway is actually listening on (or if it crashed and nothing is listening).
[Tue Feb 17 2026 14:41:02.042]
openclaw-1:
2026-02-17T07:41:02.041Z [gateway] listening on ws://[::1]:18789
[Tue Feb 17 2026 14:41:02.042]
openclaw-1:
2026-02-17T07:41:02.042Z [gateway] log file: /tmp/openclaw/openclaw-2026-02-17.log
[Tue Feb 17 2026 14:41:02.061]
openclaw-1:
2026-02-17T07:41:02.060Z [browser/service] Browser control service ready (profiles=2)
[Tue Feb 17 2026 14:41:02.130]
openclaw-1:
2026-02-17T07:41:02.130Z [telegram] [default] starting provider (@heisenbergclawbot)
[Tue Feb 17 2026 14:41:02.146]
openclaw-1:
2026-02-17T07:41:02.145Z [telegram] autoSelectFamily=false (default-node22)
[Tue Feb 17 2026 14:42:23.000]
openclaw-1:
2026-02-17T07:42:22.999Z [diagnostic] lane task error: lane=main durationMs=381 error="FailoverError: LLM request rejected: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits."
[Tue Feb 17 2026 14:42:23.001]
openclaw-1:
2026-02-17T07:42:23.000Z [diagnostic] lane task error: lane=session:agent:main:main durationMs=384 error="FailoverError: LLM request rejected: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits."
[Tue Feb 17 2026 14:43:20.100]
openclaw-1:
2026-02-17T07:43:20.100Z [diagnostic] lane task error: lane=main durationMs=237 error="FailoverError: LLM request rejected: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits."
[Tue Feb 17 2026 14:43:20.100]
openclaw-1:
2026-02-17T07:43:20.100Z [diagnostic] lane task error: lane=session:agent:main:main durationMs=239 error="FailoverError: LLM request rejected: Your c
show only likely-failure lines (last ~50 matches)
grep -nE "Uncaught|TypeError|setSession|EADDRINUSE|ERR_|FATAL|crash|exception" "$LOG" | tail -n 50
LOG=/tmp/openclaw/openclaw-2026-02-17.log
121:{"0":"[openclaw] Uncaught exception: TypeError: Cannot read properties of null (reading 'setSession')\n at TLSSocket.setSession (node:_tls_wrap:1132:16)\n at Object.connect (node:_tls_wrap:1826:13)\n at Client.connect (/usr/local/lib/node_modules/openclaw/node_modules/undici/lib/core/connect.js:70:20)\n at connect (/usr/local/lib/node_modules/openclaw/node_modules/undici/lib/dispatcher/client.js:452:21)\n at _resume (/usr/local/lib/node_modules/openclaw/node_modules/undici/lib/dispatcher/client.js:627:7)\n at resume (/usr/local/lib/node_modules/openclaw/node_modules/undici/lib/dispatcher/client.js:561:3)\n at Client.<computed> (/usr/local/lib/node_modules/openclaw/node_modules/undici/lib/dispatcher/client.js:285:31)\n at TLSSocket.onHttpSocketClose (/usr/local/lib/node_modules/openclaw/node_modules/undici/lib/dispatcher/client-h1.js:942:18)\n at TLSSocket.emit (node:events:531:35)\n at node:net:346:12","_meta":{"runtime":"node","runtimeVersion":"22.22.0","hostname":"d82489a7f4c2","name":"openclaw","date":"2026-02-17T07:44:23.278Z","logLevelId":5,"logLevelName":"ERROR","path":{"fullFilePath":"file:///usr/local/lib/node_modules/openclaw/dist/entry.js:1026:68","fileName":"entry.js","fileNameWithLine":"entry.js:1026","fileColumn":"68","fileLine":"1026","filePath":"/usr/local/lib/node_modules/openclaw/dist/entry.js","filePathWithLine":"/usr/local/lib/node_modules/openclaw/dist/entry.js:1026","method":"console.error"}},"time":"2026-02-17T07:44:23.279Z"}
root@d82489a7f4c2:~#
Two separate things happening there:
- Youβre inside the container, so
docker β¦commands wonβt exist. --urlis not a global OpenClaw flag. It only exists on specific subcommands (likeopenclaw gateway status,openclaw tui, etc).
A) Get container logs (run on the HOST, not in the container)
Back at the host prompt (root@srv1100186:~#), run:
docker logs openclaw-zs4q-openclaw-1 --tail 200
B) Correct way to override the gateway URL
Inside the container, this is valid:
openclaw gateway status --url ws://127.0.0.1:43710
If it says auth is missing (common when using --url), fetch your token then pass it:
openclaw config get gateway.auth.token
openclaw gateway status --url ws://127.0.0.1:43710 --token "<paste-token>"
C) Figure out which port is actually open (since ss isnβt installed)
Inside the container:
node -e "const net=require('net'); for (const p of [18789,43710]) { const s=net.connect(p,'127.0.0.1'); s.on('connect',()=>{console.log(p,'OPEN'); s.end();}); s.on('error',e=>console.log(p,'CLOSED',e.code)); }"
That will tell us immediately whether OpenClaw is listening on 18789 or 43710 (or neither).
Paste:
- host
docker logs β¦ --tail 200 - output of the
node -e β¦port check - (optional)
openclaw config get gateway.portandopenclaw config get gateway.bind
Then we can line up the port mapping (and from there, the Tailscale sidecar setup).
root@d82489a7f4c2:~# docker logs openclaw-zs4q-openclaw-1 --tail 200
bash: docker: command not found
root@d82489a7f4c2:~#
Yep β your log snippet confirms the actual problem:
- Gateway starts fine:
listening on ws://[::1]:18789 - Then it hard-crashes with:
Uncaught exception: TypeError: Cannot read properties of null (reading 'setSession')
at TLSSocket.setSession (node:_tls_wrap:1132:16)
... undici ...
Thatβs a Node/undici TLS crash (not a Tailscale issue). Youβre on Node v22.22.0 + OpenClaw 2026.2.12.
Also you have a second (separate) issue: your logs show Anthropic failing with βcredit balance is too lowβ, so even when the gateway is up it wonβt be able to answer until you switch providers / add credits.
What to do
1) Stop the TLS crash (workaround)
In practice the fix is: use a different Node patch / image (this kind of undici+TLS crash is version-sensitive).
Since youβre using ghcr.io/hostinger/hvps-openclaw:latest and it bundles Node 22.22.0, the quickest workaround is either:
- pull a newer image (if Hostinger updated it), or
- switch to an OpenClaw install/image where you can pin Node to a different 22.x LTS patch (or 24.x LTS).
If you can, also update off OpenClaw 2026.2.12 β 2026.2.13+.
If this is reproducible for you, itβs worth filing a GitHub issue with that stack trace + Node version: https://github.com/openclaw/openclaw/issues
2) Fix βno repliesβ: you currently have no Anthropic credits
Your gateway is trying Anthropic and getting rejected due to low balance. You need to either:
- add Anthropic credits, or
- change the default model/provider to something you have working auth for.
3) For Tailscale-in-Docker: use a sidecar (recommended)
Donβt run Tailscale inside the OpenClaw container. Run a tailscale sidecar and share the network namespace:
services:
tailscale:
image: tailscale/tailscale:stable
cap_add: ["NET_ADMIN","NET_RAW"]
devices: ["/dev/net/tun:/dev/net/tun"]
environment:
- TS_AUTHKEY=tskey-...
volumes:
- tailscale-state:/var/lib/tailscale
restart: unless-stopped
openclaw:
image: ghcr.io/hostinger/hvps-openclaw:latest
network_mode: service:tailscale
depends_on: [tailscale]
restart: unless-stopped
Then either:
- proxy with
tailscale serve(keeps OpenClaw bound to loopback), or - set OpenClaw
gateway.bind: "tailnet"(and keep auth/token on).
If you paste your current compose (or docker inspect openclaw-zs4q-openclaw-1 | grep -i OPENCLAW env section), I can tell you exactly what to change for the bind/port.
Youβre still inside the container (root@d82489a7f4c2), so docker logs β¦ will always be command not found there (the Docker CLI lives on the host).
1) Run docker logs on the host
Exit the container (exit or Ctrl-D) until youβre back at root@srv1100186:~#, then:
docker logs openclaw-zs4q-openclaw-1 --tail 200
2) Youβve got a real crash (not Tailscale)
Your /tmp/openclaw/openclaw-2026-02-17.log shows the gateway dies with:
TLSSocket.setSession β¦ undici β¦ Cannot read properties of null
Thatβs a Node/undici TLS crash. In your case itβs Node 22.22.0 + OpenClaw 2026.2.12. Best practical fix: update the image/OpenClaw (2026.2.13+) and/or use a different Node patch / Node 24 LTS. (Hostingerβs :latest image might be pinned; if so, youβll need a newer image or custom Dockerfile.)
3) For Tailscale-in-Docker: use a sidecar (recommended)
You donβt need Tailscale inside the OpenClaw container. Use a Tailscale sidecar and share netns:
openclaw:
network_mode: service:tailscale
tailscale:
image: tailscale/tailscale:stable
cap_add: ["NET_ADMIN","NET_RAW"]
devices: ["/dev/net/tun:/dev/net/tun"]
environment:
- TS_AUTHKEY=tskey-...
Then either:
- keep OpenClaw bound to loopback and do
tailscale serveto forward127.0.0.1:18789, or - set
OPENCLAW_GATEWAY_BIND=lan(ortailnet) so it listens on the Tailscale interface.