TL;DR
The OpenClaw dashboard is the Control UI, and the Gateway serves it on loopback: 127.0.0.1:18789. On the machine running the gateway, openclaw dashboard opens it with your current auth; openclaw dashboard --no-open prints the URL without launching a browser. From any other machine that address can never load, because 127.0.0.1 is loopback and always means "this machine": forward the port first with ssh -N -L 18789:127.0.0.1:18789 user@host, then open 127.0.0.1:18789 locally. Three facts cover most of what goes wrong here: the dashboard is loopback-only until you tunnel; a 401 means the token you presented is missing or not the one the gateway expects; and a gateway error gets a fixed ladder, openclaw gateway status, then openclaw health, then openclaw doctor, then logs, then openclaw gateway restart. Refreshing the tab is not a step.
Search any of these errors and you get the CLI reference plus fragments of issue threads. The reference is fine, but nothing ties "the URL from my server won't open at home", "401 invalid bearer token", and "gateway error" together. I keep a gateway and its dashboard running in production, and those are the exact spots where it breaks. My favorite incident: the panel reported the gateway offline while the bot kept answering in Telegram the whole time. That one is a token mismatch, and we'll get to it.
The dashboard is the Control UI on 127.0.0.1:18789
openclaw dashboard is the front door. The docs describe it as "Open the Control UI using your current auth", and that's accurate: the command finds your token and brings it along. It resolves gateway.auth.token (including a SecretRef, if you store the token that way) and follows gateway.tls.enabled: with TLS on, it prints an https:// URL and connects over wss://. If it can't deliver the token to your browser or clipboard, it prints a manual-auth hint that names OPENCLAW_GATEWAY_TOKEN, gateway.auth.token, and the URL fragment key token, without ever printing the token itself.
On a headless box, or when you just want the address:
openclaw dashboard --no-openThat prints the URL and stops.
The address underneath is 127.0.0.1:18789/; the root of the gateway port is the Control UI. It's also the single most common "dashboard not connecting" report, and usually nothing is broken. You open the dashboard on the server once, bookmark the full URL from the address bar, paste it into the browser on your laptop a week later, and get a connection error. 127.0.0.1 points at whatever machine you type it on; on your laptop, that URL asks your laptop for a gateway your laptop isn't running. The fix is a tunnel, and it's the next section.
Connecting to the dashboard on a remote server
The gateway's WebSocket listens on loopback, port 18789 by default, and being unreachable from outside is the security model working as intended. The supported ways in, in the order I'd use them:
An SSH tunnel. My default, because it assumes nothing beyond the SSH access you already have:
ssh -N -L 18789:127.0.0.1:18789 user@hostLeave that running, then open 127.0.0.1:18789 in your local browser, or run openclaw dashboard on your laptop: your local loopback port now forwards to the gateway host's. On macOS you can keep the tunnel alive permanently with a LaunchAgent; I'll leave those details to the SSH docs.
The tunnel carries the CLI too. With it up, openclaw health, openclaw status --deep, and openclaw gateway status / health / probe all reach the remote gateway over ws://127.0.0.1:18789, so every diagnostic on this page works from your laptop as if you were sitting on the box.
Tailscale Serve, if the machine lives on your tailnet.
A trusted LAN or tailnet bind, if you deliberately bind wider than loopback. No exceptions on this one: a non-loopback bind must carry gateway auth, a token, a password, or a trusted proxy. Provisioning the box itself, and what happens to boxes that skip this, is covered in how-to-run-openclaw-on-a-vps.
401: missing authentication header / invalid bearer token
A 401 is precise: the gateway wanted its auth token and got nothing, or got the wrong one. The entire puzzle is where the token is supposed to come from.
For local connections the precedence is OPENCLAW_GATEWAY_TOKEN, then gateway.auth.token, then gateway.remote.token.
One documented exception catches people constantly: --url does not read credentials from config or environment. The moment you point a command at an explicit --url, you pass --token or --password yourself. No flag, no auth, 401.
Two commands narrow it down fast:
openclaw channels status --probe
openclaw doctor --fixThe probe shows live transport status per account, so an auth failure surfaces immediately instead of hiding behind a generic "offline". Doctor checks for stale credentials and state, and --fix repairs what it safely can.
Then the production classic. On the 2026.6.x line, the managed gateway service resolves gateway.auth.token from ~/.openclaw/openclaw.json first, while direct local clients prefer the env token. So the OPENCLAW_GATEWAY_TOKEN you exported months ago and still consider authoritative can quietly lose to the config file. The symptom looks self-contradictory: channels work, the bot answers, and the panel insists the gateway is offline while the logs fill with token_mismatch. Nothing crashed; two components are presenting two different tokens. The durable fix is to keep the token in config, in gateway.auth.token, so it survives restarts and every consumer resolves the same value.
If your error talks about pairing rather than a bearer token, you're in a different subsystem entirely: device and channel pairing has its own rules and its own guide, openclaw-gateway-pairing-required.
"Gateway error" or no response: run the ladder
"Gateway error" spans everything from a dead process to a crash loop, so don't guess. Run these in order:
openclaw gateway status --json
openclaw health
openclaw status --deep
openclaw doctoropenclaw gateway status says whether the process and its RPC are up; --require-rpc makes it strict, --json makes it scriptable. openclaw health and openclaw status --deep fill in the rest of the picture. openclaw doctor inspects config and state, and openclaw doctor --fix repairs. Then read the logs. Then, and only then:
openclaw gateway restartThe order matters because a restart destroys evidence. And refreshing the browser tab appears nowhere on the ladder, because the tab is a client of the gateway; if the gateway is wedged, the freshest tab in the world will just report it more recently.
The scary one is RangeError: maximum call stack size exceeded. The docs don't explain it, and I'm not going to invent a root cause. Treat it as its class, "the gateway crashed or looped", and run the same ladder. One pattern is worth acting on, though: if the crash showed up right after an update, suspect a version regression and hold your last known-good version until it's resolved. Pinning and rolling back are covered in how-to-update-openclaw.
Restarting the gateway, with the flags that matter
openclaw gateway restart is the plain form. The flags are where you make actual decisions:
openclaw gateway restart --safedrains in-flight work before restarting;--safe --skip-deferraltakes the safe path without waiting on deferred work.openclaw gateway restart --forcefor a gateway that's hung and won't drain.openclaw gateway restart --wait 30soverrides the drain budget. A bare number is milliseconds,s/m/hsuffixes work, and--wait 0waits indefinitely.
openclaw gateway stop, start, and run cover the coarser cases, and openclaw gateway probe (--json, and --ssh user@host to probe through SSH) is the quick reachability check.
Two footnotes that trip people up. First, commands.restart is enabled by default, and SIGUSR1 triggers an in-process restart; if someone set commands.restart: false, manual restarts are blocked, and that's configuration, not breakage. Second, if openclaw gateway install didn't leave a running service behind, openclaw gateway install --force reinstalls it (there's a --wrapper variant).
And the most important sentence in the gateway docs, quoted exactly: "Config changes require a gateway restart." Saving the file changes nothing until the gateway restarts; in my experience half of all "my change didn't apply" reports are this one sentence. One nuance: the messages config hot-reloads on save, and needs a restart only when file-watching or config-reload is disabled, or the watcher has died, in which case a restart applies what you saved. Native-plugin config always takes a restart.
The gateway speaks WebSocket, not plain HTTP
The control plane is a WebSocket endpoint, ws://127.0.0.1:18789. A flat browser GET or a bare curl at that port does not behave like a normal web page, and whatever it shows you is not a verdict on gateway health. Check the gateway with tools that speak its protocol, openclaw dashboard and openclaw health, and don't read tea leaves out of a raw GET.
What production taught me
- Token drift is the number one "sudden 401". After restarts, the layer that talks to the gateway and the gateway itself can end up holding different tokens. The tell is contradiction: panel offline, bot answering. Compare the token being presented against the one the gateway actually resolves before you restart anything in a loop.
- One gateway per host. A second gateway instance fighting for port 18789 presents as "the dashboard is flaky".
- Config is live after a restart, not after a save. Worth repeating, because the file on disk looks so convincingly done.
- Loopback plus an SSH tunnel closes almost the entire surface. The only thing facing the internet is SSH itself. I've never regretted this default.
If you'd rather have this as a panel: Assistant Control
Disclosure first: this section is about Cain, which I build. It is not an OpenClaw feature, so weigh it accordingly.
Assistant Control is the control room over the runtime: agents, memory, Life Feed, briefings and system settings in one panel, while day-to-day conversation stays in Telegram. Setup is one button, and the gateway and its token are wired for you, which kills both problems this article is about: no token to drift, and no SSH tunnel to keep alive. You reach the panel at a private, access-controlled link (Cloudflare Access), so only you get in, and the box keeps no inbound ports open. It's the same loopback principle this article preaches, run for you. From the panel you switch which subscription powers the runtime: the ChatGPT/Codex or Claude plan you already pay for, calling the provider directly. Telegram connects from the panel too, including group chats, with no CLI pairing by hand (the mechanics of that live in openclaw-telegram-bot-not-working). No terminals, configs or server admin.
It runs on a dedicated server provisioned for you, one client per server, on a pinned build that went through our upgrade matrix rather than whatever shipped last night. And it's one option, not the only right way. Everything the panel does, this page just taught you to do by hand; if a terminal is a comfortable place for you, the SSH tunnel is one line and you genuinely don't need a product for it.
When it isn't OpenClaw at all
Before you file an issue, four failures that look identical from your side:
- The SSH tunnel died. Your local
127.0.0.1:18789goes dark and looks exactly like a gateway crash. Check the tunnel process first; restart it before you restart anything on the server. - A second gateway on the same port. Another instance squatting on 18789 makes the dashboard misbehave in ways no gateway fix will cure. One gateway per host.
- The wrong token in this shell. The gateway is fine; the environment you're testing from is presenting a stale token. Walk the precedence above before assuming a server-side problem.
- Plain network trouble. If SSH itself can't reach the box, a tunnel won't help you, and no dashboard symptom means anything until connectivity is back.
FAQ
Why doesn't the OpenClaw dashboard / `127.0.0.1:18789` load? Because it's a loopback WebSocket Control UI on the gateway host. On that host, openclaw dashboard opens it. From anywhere else, 127.0.0.1:18789 is unreachable by definition; forward the port with ssh -N -L 18789:127.0.0.1:18789 user@host, then open 127.0.0.1:18789 on your own machine.
How do I open the OpenClaw Control UI? openclaw dashboard opens it using your current auth. openclaw dashboard --no-open prints the URL without launching a browser, which is the useful form on servers and over SSH.
How do I connect to the dashboard on a remote server or VPS? Keep the gateway on loopback and bring the port to you: ssh -N -L 18789:127.0.0.1:18789 user@host, or Tailscale Serve on a tailnet. If you bind wider than loopback, set gateway.auth.token; and gateway.auth.mode="none" on anything internet-facing, never.
What does "401 missing authentication header / invalid bearer token" mean? The gateway expects its auth token and you presented none, or the wrong one. On 2026.6.x the managed service reads gateway.auth.token from config first, so an exported OPENCLAW_GATEWAY_TOKEN can lose to it. openclaw channels status --probe makes the 401 visible; openclaw doctor --fix checks for stale credentials. Keep the token in gateway.auth.token.
How do I restart the OpenClaw gateway? openclaw gateway restart; --safe drains work cleanly, --force handles a hung process, and --wait 30s adjusts the drain budget. stop, start, and status exist for the coarser cases. Remember that config changes apply on restart.
Where do I start on `openclaw gateway error` or `RangeError: maximum call stack size exceeded`? See what's alive: openclaw gateway status --json, openclaw health, openclaw doctor, then logs, then openclaw gateway restart. If the crash began right after an update, suspect a version regression and go back to your known-good version; see how-to-update-openclaw.
Can I reach the dashboard with `--url` and no token? No. --url doesn't read credentials from config or environment; pass --token or --password explicitly. And never expose a gateway with auth.mode="none".
Ilya Prudnikov runs private OpenClaw and Hermes instances. These are an operator's notes on the dashboard and gateway, not project documentation; commands, defaults, and ports drift, so check OpenClaw's current docs for the authoritative version.

