#Official setup fails on Raspberry Pi + Docker (completed but has issues)

1 messages · Page 1 of 1 (latest)

digital hinge
#

OPENCLAW docker setup flow:

cd docker
git clone https://github.com/openclaw/openclaw.ai
cd openclaw
nano docker-compose.yml (fix: allow docker cli container to connect to gateway: https://github.com/openclaw/openclaw/pull/12504/changes)
./docker-setup.sh

result:
When prompted:
  - Gateway bind: lan
  - Gateway auth: token
  - Gateway token: THE_TOKEN
  - Tailscale exposure: Off
  - Install Gateway daemon: No

docker compose cp openclaw-gateway:/home/node/.openclaw/openclaw.json ./openclaw.config
nano openclaw.config --> put the token where needed in the config: THE_TOKEN
docker compose cp ./openclaw.config openclaw-gateway:/home/node/.openclaw/openclaw.json
nano .env:
add:
CLAUDE_AI_SESSION_KEY=
CLAUDE_WEB_COOKIE=
CLAUDE_WEB_SESSION_KEY=

nano docker-compose.yml (edits on cli):
add this row under volumes to enable sandboxes later: - /var/run/docker.sock:/var/run/docker.sock
add a new section tmpfs:
-/tmp
add a new section read_only: true

nano docker-compose.yml (edits on gateway):
update ports section like this:
ports:
- "127.0.0.1:18789:18789"
- "127.0.0.1:18790:18790"

docker compose down && docker compose up -d

to make the webUI device-pairing:
docker compose exec openclaw-gateway node dist/index.js devices list
docker compose exec openclaw-gateway node dist/index.js devices approve --latest

docker compose exec openclaw-gateway node dist/index.js status
Result of the last command (gateway section):

│ Gateway │ local · wss://127.0.0.1:18789 (local loopback) · unreachable (connect failed: self-signed certificate)

And the agents seems to be unable to use the cronjobs from the application itself or browsing the web with a headless browser or do tasks on the system itself.

clever condorBOT
#

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

patent violetBOT
#

That unreachable (connect failed: self-signed certificate) line is almost always: your Gateway is running with TLS enabled (wss://…) using an auto-generated self-signed cert, and the client doing the probe (CLI/UI/other container) is refusing it.

1) Quick fix (recommended for Docker-on-LAN / localhost-published ports)

If you’re only exposing the port as 127.0.0.1:18789:18789 anyway, you usually don’t need Gateway TLS. Let Docker/SSH/Tailscale/reverse-proxy handle TLS instead.

In openclaw.json set:

{
  gateway: {
    tls: { enabled: false },
  },
}

Then docker compose down && docker compose up -d and re-run:

#
docker compose exec openclaw-gateway node dist/index.js status

You should see ws://… and reachable (no self-signed error).

(Ref: the config schema does have gateway.tls.enabled + autoGenerate etc; it’s just easy to miss in older docs. Related reading: https://docs.openclaw.ai/gateway/remote and https://docs.openclaw.ai/gateway/security)

2) If you want to keep wss:// (TLS-on-Gateway)

Then you need to pin the cert fingerprint for clients that connect to it (because it’s self-signed).

Please paste these (scrub tokens/passwords), and I’ll tell you exactly where to put the fingerprint in your setup:

docker compose exec openclaw-gateway node dist/index.js --version
docker compose exec openclaw-gateway node dist/index.js gateway status --json
#
docker compose exec openclaw-gateway node dist/index.js config get gateway --json

You’re looking for a fingerprintSha256 / tlsFingerprint-ish field in the JSON, and then you set it for clients via gateway.remote.tlsFingerprint (docs mention it here: https://docs.openclaw.ai/gateway/remote).


3) “Agents can’t do cron / browser / system tasks”

Once the gateway connectivity is clean, the next likely culprit on Docker is sandbox + tool policy (especially in Discord/group contexts where sessions are often sandboxed).

Run:

docker compose exec openclaw-gateway node dist/index.js sandbox explain --json
docker compose exec openclaw-gateway node dist/index.js cron status
#

and paste the output. Common outcomes:

  • Sandboxed but no Docker socket in the gateway container → exec/browser-style tools won’t work. Mounting /var/run/docker.sock into the cli container won’t help if the gateway is the one trying to start sandboxes.
  • Tool allowlist blocks cron/browser/exec → needs tools.allow (or tools.sandbox.tools.allow) adjusted. (Good explainer: https://docs.openclaw.ai/gateway/sandbox-vs-tool-policy-vs-elevated)

Also: on Raspberry Pi, the headless browser tool may fail simply because Chromium/deps aren’t available for that container/arch. The browser status output will make that obvious after we fix the gateway connection.

Send the 3 JSON outputs + sandbox explain, and we can get this to a concrete diff instead of guessing.

digital hinge
#

Stripped something not important due to message limits:
docker ... --version
2026.2.21
docker ... gateway status --json
{
"service": {
"label": "systemd",... "runtime": {
"status": "unknown",
"detail": "Error: systemctl --user unavailable: spawn systemctl EACCES"
},
"configAudit": {
"ok": false,
"issues": [
{
"code": "gateway-path-missing",
"message": "Gateway service PATH is not set; the daemon should use a minimal PATH.",
"level": "recommended"
}
]
}
},
"config": {
"cli": {
"path": "/../openclaw.json",
"exists": true,
"valid": true
},
"daemon": {
"path": "/../openclaw.json",
"exists": true,
"valid": true
}
},
"gateway": {
"bindMode": "lan",
"bindHost": "0.0.0.0",
"port": 18789,
"portSource": "env/config",
"probeUrl": "ws://192.168.176.2:18789",
"probeNote": "bind=lan listens on 0.0.0.0 (all interfaces); probing via 192.168.176.2."
},
"port": {
"port": 18789,
"status": "busy",
"listeners": [],
"hints": [
"Port is in use but process details are unavailable (install lsof or run as an admin user)."
]
},
"rpc": {
"ok": false,
"error": "SECURITY ERROR: Gateway URL "ws://192.168.176.2:18789" uses plaintext ws:// to a non-loopback address.\nBoth credentials and chat data would be exposed to network interception.\nSource: cli --url\nConfig: /home/node/.openclaw/openclaw.json\nFix: Use wss:// for the gateway URL, or connect via SSH tunnel to localhost.",
"url": "ws://192.168.176.2:18789"
},
"extraServices": []
}
docker ... config get gateway --json
{
"port": 18789,
"mode": "local",
"bind": "lan",
"auth": {
"mode": "token",
"token": THE_TOKEN
},
"tailscale": {
"mode": "off",
"resetOnExit": false
},
"tls": {
"enabled": true,
"autoGenerate": true
},
"nodes": {
"denyCommands": [... ]
}
}

#

And for now I am stuck with the connection problem, I wanna keep TLS enabled, since also the browsers do not "like" anymore clean Http connections

#

By the way I tried also the first approach, just for the sake of testing, and it did not fix the issue: after disabling the TLS this is the result (from the status command): │ Gateway │ local · ws://127.0.0.1:18789 (local loopback) · unreachable (connect failed: connect ECONNREFUSED 127.0.0.1:18789)

patent violetBOT
#

ECONNREFUSED 127.0.0.1:18789 means “nothing is listening there”.

In Docker setups there are two super-common causes:

A) You’re running the command in a different container than the gateway

If you run status in the cli container, 127.0.0.1 is the cli container, not the gateway container → refused.

Try (explicitly target the gateway service name):

docker compose exec openclaw-cli node dist/index.js status --url ws://openclaw-gateway:18789
# or whatever your cli service is called

B) The gateway is bound to loopback inside the container

If the gateway binds to 127.0.0.1 inside the container (gateway.bind: "loopback"), then host port publishing usually forwards to the container’s eth0 IP, and the process won’t accept it → refused.

#

Inside the gateway container, check what it’s actually listening on:

docker compose exec openclaw-gateway sh -lc 'ss -lntp | grep 18789 || true'
docker compose logs openclaw-gateway --tail 200

If you see it only listening on 127.0.0.1:18789 (or not listening at all), set the gateway bind to LAN inside the container:

{
  gateway: {
    bind: "lan",      // 0.0.0.0 inside container
    port: 18789,
    tls: { enabled: false },
    auth: { mode: "token", token: "..." }
#
  }
}

…and keep your docker-compose port publish restricted to host loopback (your 127.0.0.1:18789:18789 is perfect).


Quick clarification (so I don’t guess): where are you running status from?

  1. On the Pi host (openclaw status), or
  2. via docker compose exec ... (and if so, which service/container)?

If you paste:

  • docker compose ps
  • docker compose logs openclaw-gateway --tail 200
  • and the ss -lntp output above
#

…I can tell you which of A/B it is in one pass.

digital hinge
#

I am running the status command from the gateway by using: docker compose exec openclaw-gateway node dist/index.js status

#

I have already the gateway bind set to LAN, as you can see above from my config

patent violetBOT
#

If you’re running status inside the same openclaw-gateway container, then ws://127.0.0.1:18789 should work as long as the gateway process is actually listening on that port in that container.

ECONNREFUSED = there is no listener on 127.0.0.1:18789 (either the gateway isn’t running, or it’s listening on a different port, or you’re looking at a different config than the process is using).

Can you run these and paste the outputs?

# 1) What is PID 1 in this container actually running?
docker compose exec openclaw-gateway sh -lc 'tr "\0" " " </proc/1/cmdline; echo'

# 2) What ports are actually listening?
docker compose exec openclaw-gateway sh -lc 'ss -lntp | sed -n "1,200p"'

# 3) What does *your CLI inside the container* think the gateway config is?
# (this does NOT require the gateway to be reachable; it just reads config)
docker compose exec openclaw-gateway sh -lc 'node dist/index.js config get gateway --json'
#

# 4) Recent gateway logs (usually contains “listening on …” or a crash reason)
docker compose logs openclaw-gateway --tail 200

What I’m looking for:

  • If ss shows it’s listening on 18790 (or some other port), then your CLI is just targeting the wrong URL/port.
  • If ss shows nothing on 18789/18790, the gateway process likely crashed or never started — logs will say why (config parse error, permissions, etc.).
  • If PID 1 is not the gateway (e.g. sleep / some supervisor), then the container might not actually be running the gateway server at all.

Once we get it reachable again, we can tackle cron/browser/system tools (those are usually sandbox/tool-policy + docker socket placement), but right now the hard blocker is simply “no WS server on that port.”

#

gateway.bind: "lan" is fine — but ECONNREFUSED from inside the same container means something simpler: the gateway process is not actually listening on 18789 (crashed, never started, or listening on a different port).

Can you paste the output of these 4 commands?

# 1) Is anything listening on the expected ports?
docker compose exec openclaw-gateway sh -lc 'ss -lntp | grep -E ":(18789|18790)\\b" || true'

# 2) What does the container think the gateway config is? (does NOT need gateway running)
docker compose exec openclaw-gateway sh -lc 'node dist/index.js config get gateway --json'

# 3) Is the container restarting / unhealthy?
docker compose ps

# 4) Why did it not start / why did it crash?
docker compose logs openclaw-gateway --tail 200
#

A couple of gotchas this will reveal immediately:

  • If ss shows nothing on :18789 → the gateway didn’t start (logs will say why; common is a config/schema error or a filesystem/permission issue).
  • If it’s listening on a different port than 18789 → gateway.port is different than you think (the config get gateway --json will show it).
  • Also: port 18790 is legacy in newer builds (gateway is typically one multiplexed port, default 18789), so don’t worry if nothing is on 18790.

Once we see those outputs, we can pinpoint the exact fix instead of guessing.

digital hinge
#

docker compose exec openclaw-gateway sh -lc 'tr "\0" " " </proc/1/cmdline; echo'
/sbin/docker-init -- docker-entrypoint.sh node dist/index.js gateway --bind lan --port 18789
docker compose exec openclaw-gateway sh -lc 'ss -lntp | sed -n "1,200p"'
sh: 1: ss: not found
docker compose exec openclaw-gateway sh -lc 'node dist/index.js config get gateway --json'
^C{
"port": 18789,
"mode": "local",
"bind": "lan",
"auth": {
"mode": "token",
"token": THE_TOKEN
},
"tailscale": {
"mode": "off",
"resetOnExit": false
},
"tls": {
"enabled": false,
"autoGenerate": true
},
"nodes": {
"denyCommands": [
"camera.snap",
"camera.clip",
"screen.record",
"calendar.add",
"contacts.add",
"reminders.add"
]
}
}

#

it is strange because since I can access the webUI via SSH tunneling, I imagine the port is correct or would not work either.

patent violetBOT
#

bind: "lan" is good — but it doesn’t explain ECONNREFUSED.

Given what you pasted, PID 1 is launching the gateway:

node dist/index.js gateway --bind lan --port 18789

So the only ways you still get “refused” from inside the same container are basically:

  1. the gateway never actually got to “listen()” (crash / exit / stuck), or
  2. it’s listening on IPv6 only (::1 / ::) and not on 127.0.0.1.

Because ss isn’t installed in that image, do these quick tests (no extra packages):

1) Check if it’s listening on IPv6

docker compose exec openclaw-gateway sh -lc 'node dist/index.js status --url ws://[::1]:18789'
#
  • If this works, but ws://127.0.0.1:18789 fails → it’s an IPv6 bind issue.

2) See if anything is listening on 18789 (via /proc)

18789 in hex is 4965:

docker compose exec openclaw-gateway sh -lc "grep -i ':4965' /proc/net/tcp /proc/net/tcp6 || true"

If you see a line with state 0A that’s LISTEN.

3) Get the gateway container logs (this is usually the smoking gun)

docker compose logs openclaw-gateway --tail 200
docker compose ps
#

Also: please don’t paste the real gateway token in the thread. If you think you might’ve posted the real one earlier, rotate it afterward.

Paste results of (1) + (2) + the last ~50 lines of the gateway logs and I can tell you which branch you’re in and the exact config change to force a sane bind.

digital hinge
#

🦞 OpenClaw 2026.2.21 (unknown) — Greetings, Professor Falken

error: unknown option '--url'
2)

/proc/net/tcp: 1: 00000000:4965 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 46674953 1 000000008d6b5751 100 0 0 10 0

openclaw-openclaw-gateway-1 | 2026-02-22T01:45:48.906Z [canvas] host mounted at http://0.0.0.0:18789/__openclaw__/canvas/ (root /home/node/.openclaw/canvas)
openclaw-openclaw-gateway-1 | 2026-02-22T01:45:49.248Z [heartbeat] started
openclaw-openclaw-gateway-1 | 2026-02-22T01:45:49.261Z [health-monitor] started (interval: 300s, grace: 60s)
openclaw-openclaw-gateway-1 | 2026-02-22T01:45:49.279Z [gateway] agent model: openai/gpt-5.2
openclaw-openclaw-gateway-1 | 2026-02-22T01:45:49.286Z [gateway] listening on ws://0.0.0.0:18789 (PID 9)
openclaw-openclaw-gateway-1 | 2026-02-22T01:45:49.304Z [gateway] log file: /tmp/openclaw/openclaw-2026-02-22.log
openclaw-openclaw-gateway-1 | 2026-02-22T01:45:49.757Z [browser/service] Browser control service ready (profiles=2)
openclaw-openclaw-gateway-1 | 2026-02-22T01:45:51.710Z [hooks:loader] Registered hook: boot-md -> gateway:startup
openclaw-openclaw-gateway-1 | 2026-02-22T01:45:51.724Z [hooks:loader] Registered hook: bootstrap-extra-files -> agent:bootstrap
openclaw-openclaw-gateway-1 | 2026-02-22T01:45:51.739Z [hooks:loader] Registered hook: command-logger -> command
openclaw-openclaw-gateway-1 | 2026-02-22T01:45:51.784Z [hooks:loader] Registered hook: session-memory -> command:new, command:reset
openclaw-openclaw-gateway-1 | 2026-02-22T01:45:51.795Z [hooks] loaded 4 internal hook handlers
openclaw-openclaw-gateway-1 | 2026-02-22T01:45:57.901Z [telegram] [default] starting provider (@tg_bot)
openclaw-openclaw-gateway-1 | 2026-02-22T01:45:57.948Z [telegram] autoSelectFamily=true (default-node22)
openclaw-openclaw-gateway-1 | 2026-02-22T01:51:06.457Z [ws] ⇄ res ✓ config.get 1515ms conn=d7671a13…e12f id=a6fdf114…964a

#

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6ca18d0ca656 openclaw:local "docker-entrypoint.s…" 25 minutes ago Up 25 minutes 127.0.0.1:18789-18790->18789-18790/tcp openclaw-openclaw-gateway-1
ac0aa33ed54c f4a33730cf5c "node dist/index.js …" 36 hours ago Up 36 hours openclaw_openclaw-cli_run_ee8f96846e67

patent violetBOT
#

Cool, that ps output is actually super helpful: the gateway container is up, and PID 1 is indeed node dist/index.js gateway ....

At this point, ECONNREFUSED from inside the same container basically means: the gateway never opened a listening socket on 18789 (or it’s listening somewhere unexpected).

Since ss isn’t available in the image, can you run these exact checks and paste the output?

1) Is port 18789 listening? (no extra tools needed)

18789 = 0x4965

docker compose exec openclaw-gateway sh -lc "grep -i ':4965' /proc/net/tcp /proc/net/tcp6 || true"

If you see a line ending in :4965 with state 0A that means LISTEN.

2) Show the gateway logs (this usually reveals the reason immediately)

#
docker compose logs openclaw-gateway --tail 200

3) Sanity-check: try connecting to the container’s eth0 IP (not loopback)

Sometimes the probe picks 127.0.0.1 but the process is only reachable via the container IP (shouldn’t happen with bind: lan, but worth testing quickly):

docker compose exec openclaw-gateway sh -lc 'node -e "const os=require(\"os\");const nets=os.networkInterfaces();console.log(nets.eth0?.find(n=>n.family===\"IPv4\")?.address || \"no-eth0-ip\")"'

Take the printed IP and try:

docker compose exec openclaw-gateway node dist/index.js status --url ws://<THAT_IP>:18789
#

If /proc/net/tcp* shows no listener, the log tail will tell us why the gateway didn’t reach the “listening on …” stage (config validation error, cannot write state dir, crash loop, etc.).

digital hinge
#

/proc/net/tcp: 1: 00000000:4965 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 46674953 1 000000008d6b5751 100 0 0 10 0
2) already posted above
3) 192.168.192.2
docker compose exec openclaw-gateway node dist/index.js status --url ws://192.168.192.2:18789

🦞 OpenClaw 2026.2.21 (unknown) — Works on Android. Crazy concept, we know.

error: unknown option '--url'

patent violetBOT
#

That /proc/net/tcp line is actually the key signal:

00000000:4965 ... 0A ... = LISTEN on 0.0.0.0:18789 inside the gateway container.

So TCP is up, and a connect to 127.0.0.1:18789 from inside the same container should not be ECONNREFUSED. That means either:

  • the earlier ECONNREFUSED was from before the gateway finished starting / before the restart, or
  • status is a bit misleading here and we need to probe with the commands that support explicit URLs.

Also: status does not support --url (that’s why you saw unknown option '--url'). Use gateway health or gateway status for URL overrides.

Do this from inside the gateway container (don’t paste the token)

docker compose exec openclaw-gateway sh -lc '
TOKEN=$(node dist/index.js config get gateway.auth.token | tr -d "\"");
node dist/index.js gateway health --url ws://127.0.0.1:18789 --token "$TOKEN"
#
'

If that prints healthy output, then the gateway is reachable and we can move on to the cron/browser/sandbox stuff.

If it still errors, then please paste:

docker compose logs openclaw-gateway --tail 200
docker compose exec openclaw-gateway node dist/index.js status --all

(You can redact secrets; don’t post the token.)

digital hinge
#

first command:
🦞 OpenClaw 2026.2.21 (unknown) — If you're lost, run doctor; if you're brave, run prod; if you're wise, run tests.

│
â—‡
Gateway Health
OK (8518ms)
Telegram: ok (@

but my question is if this is the wrong check because we want the gateway to talk with cli and viceversa

#

no wait, it did not work. replacing the last variable TOKEN with the token, worked as I pasted. but just launching it as-is, gave error:

🦞 OpenClaw 2026.2.21 (unknown) — We ship features faster than Apple ships calculator updates.

│
gateway connect failed: Error: unauthorized: device token mismatch (rotate/reissue device token)
â—‡
Error: gateway closed (1008): unauthorized: device token mismatch (rotate/reissue device token)
Gateway target: ws://127.0.0.1:18789
Source: cli --url
Config: /home/node/.openclaw/openclaw.json

#

but lanching this: docker compose exec openclaw-gateway node dist/index.js config get gateway.auth.token

gave the correct token...

patent violetBOT
#

That LISTEN on 0.0.0.0:18789 inside the gateway container + ECONNREFUSED 127.0.0.1:18789 is almost always just network-namespace confusion:

  • 127.0.0.1 inside openclaw-cli (or any other container) means “this container”, not “the gateway container”.
  • So if you run status / gateway health from the CLI container while your URL is ws://127.0.0.1:18789, you’ll get ECONNREFUSED even though the gateway is fine.

1) Use the right URL depending on where you run the command

  • From the host (Pi itself): ws://127.0.0.1:18789 (matches your loopback-only port publish)
  • From another container on the compose network: ws://openclaw-gateway:18789 (compose service DNS name)

2) Do 2 quick “health” probes (no token paste needed)

Run these and paste the outputs (they shouldn’t include secrets):

A) From inside the gateway container (proves the gateway is actually responding):

docker compose exec openclaw-gateway sh -lc '
TOKEN="$(node dist/index.js config get gateway.auth.token | tr -d "\r\n")"
#
node dist/index.js gateway health --url ws://127.0.0.1:18789 --token "$TOKEN" --json
'

B) From inside the CLI container (proves container→container routing):

docker compose exec openclaw-cli sh -lc '
TOKEN="$(node dist/index.js config get gateway.auth.token | tr -d "\r\n")"
node dist/index.js gateway health --url ws://openclaw-gateway:18789 --token "$TOKEN" --json
'

If you don’t have a long-running openclaw-cli service, use:

docker compose run --rm openclaw-cli sh -lc '…same script…'
#

3) If you still hit “device token mismatch”

That’s usually a stale cached device token in the client state dir (common after resets/re-pairing). The clean fix is:

  • in the environment that’s failing (CLI container), move aside the cached device auth file and re-pair:
docker compose exec openclaw-cli sh -lc '
ls -la ~/.openclaw/identity || true
mv ~/.openclaw/identity/device-auth.json ~/.openclaw/identity/device-auth.json.bak 2>/dev/null || true
'

Then rerun openclaw devices list / approve again.


If you paste the JSON outputs from (A) and (B) plus docker compose logs openclaw-gateway --tail 120, we can tell immediately whether you’re fully “RPC healthy” and move on to cron/sandbox/browser.

#

Good — that means config get gateway.auth.token itself is working.

Two gotchas that still commonly explain “works when I paste it, fails when I use $TOKEN” in this Docker setup:

1) You might be reading the token in one container, but using it in another

Each container has its own ~/.openclaw/… unless you’ve explicitly volume-mounted it.

Can you run this in both containers and confirm they match?

docker compose exec openclaw-gateway sh -lc 'node dist/index.js config get gateway.auth.token | sha256sum'
docker compose exec openclaw-cli     sh -lc 'node dist/index.js config get gateway.auth.token | sha256sum'

(sha256 output is safe to paste; it doesn’t reveal the token.)

If those hashes differ, you’ve found the issue: CLI is using a different config/state than the gateway.

#

2) Trim newline/CR when putting it in a variable

Even if it prints right, command-substitution can include a trailing newline/CR depending on how the value is produced/transported.

Use this pattern:

docker compose exec openclaw-gateway sh -lc '
TOKEN="$(node dist/index.js config get gateway.auth.token | tr -d "\r\n")"
node dist/index.js gateway health --url ws://127.0.0.1:18789 --token "$TOKEN" --json
'

Bonus: don’t use 127.0.0.1 from the CLI container

If you’re probing from openclaw-cli, ws://127.0.0.1:18789 points at the CLI container itself. Use:

#
--url ws://openclaw-gateway:18789

If the error you see is specifically “device token mismatch…”

That can happen even with a correct gateway token (it’s a cached device token). Fix is usually to move aside the cached file in the failing environment (often the CLI container):

docker compose exec openclaw-cli sh -lc '
mv ~/.openclaw/identity/device-auth.json ~/.openclaw/identity/device-auth.json.bak 2>/dev/null || true
'

If you paste the exact failing command + exact error text, I can tell which of the above is the real culprit.

digital hinge
#

docker compose run --rm openclaw-cli sh -lc 'node dist/index.js config get gateway.auth.token | sha256sum'
[+] Running 1/0
â ż Container openclaw-openclaw-gateway-1 Running 0.0s
error: unknown command 'sh'

#

btw the problem was on reachability. why don't we come back to the start of this thread? it seems to me we're going around elsewhere. also you gave me some comands with exec on openclaw-cli, that is used only with run -rm generally at runtime and nothing else...

#

let's fix as first thing the TLS issue

#

step by step

#

I have the TLS enabled, but I had that self-signed cert error in the status...

patent violetBOT
#

Yep — let’s reset and go back to reachability first. And you’re right about openclaw-cli: if you normally only use it via docker compose run --rm, then my earlier exec openclaw-cli … commands were the wrong shape for your setup.

The core thing we need to nail down

Where is the client running from? Because 127.0.0.1 means different things:

  • From the Pi host: ws://127.0.0.1:18789 hits the published port (since you mapped it to host loopback).
  • From inside any other container: ws://127.0.0.1:18789 points to that container, not the gateway → commonly gives ECONNREFUSED.
  • Container-to-container should use: ws://openclaw-gateway:18789 (compose service DNS).

Do these 3 reachability checks (in order)

1) Is the port actually published on the Pi host?

On the Pi host:

docker compose ps

You should see something like 127.0.0.1:18789->18789/tcp.

#

Then a dumb TCP connect test (also on the Pi host):

python3 - <<'PY'
import socket
s=socket.socket()
s.settimeout(2)
try:
s.connect(("127.0.0.1",18789))
print("OK: host can TCP-connect to 127.0.0.1:18789")
except Exception as e:
print("FAIL:",e)
finally:
s.close()
PY

2) Does the Gateway respond to RPC from inside its own container?

#
docker compose exec openclaw-gateway sh -lc '
TOKEN="$(node dist/index.js config get gateway.auth.token | tr -d "\r\n")"
node dist/index.js gateway health --url ws://127.0.0.1:18789 --token "$TOKEN"
'

3) Does the CLI (ephemeral) reach the Gateway over the docker network?

Run this from the Pi host shell:

TOKEN="$(docker compose exec -T openclaw-gateway node dist/index.js config get gateway.auth.token | tr -d '\r\n')"
docker compose run --rm -e OPENCLAW_GATEWAY_TOKEN="$TOKEN" openclaw-cli \
node dist/index.js gateway health --url ws://openclaw-gateway:18789 --token "$OPENCLAW_GATEWAY_TOKEN"

What to paste back

  • output of (1) docker compose ps
#
  • output of the python TCP test
  • output of gateway health (2) + (3)

Once those are green, we’re actually past “reachability” and can move on to cron/tools/browser without guessing.

digital hinge
#

docker ps (i left all open no 127.0.0.1 bind for the tests):
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f5efc0455c53 openclaw:local "docker-entrypoint.s…" 30 minutes ago Up 30 minutes 0.0.0.0:18789-18790->18789-18790/tcp openclaw-openclaw-gateway-1
ac0aa33ed54c f4a33730cf5c "node dist/index.js …" 45 hours ago Up 45 hours openclaw_openclaw-cli_run_ee8f96846e67

python3 - <<'PY'
import socket
s=socket.socket()
s.settimeout(2)
try:
s.connect(("127.0.0.1",18789))
print("OK: host can TCP-connect to 127.0.0.1:18789")
except Exception as e:
print("FAIL:",e)
finally:
s.close()
PY
OK: host can TCP-connect to 127.0.0.1:18789
Ok I am getting gateway OK and also the status command now reaturn all healthy OK. let's enable the TLS now in the proper way to make the cli container still work

digital hinge
#

ok now I left everything as-is, with a OK healthy status command from the gateway-cli, and I just set TLS enabled true in the openclaw.json, and after a restart I get:
│ Gateway │ local · wss://127.0.0.1:18789 (local loopback) · unreachable (connect failed: self-signed certificate)

from docker compose run --rm openclaw-cli status