Skip to content

OpenClaw Docker: Skip the Setup Script (2026 Edition)

Most Docker guides just run docker-setup.sh and call it done. Here's the manual path that actually shows you what breaks - and how to fix it before it does.

7 min readIntermediate

Here’s an unpopular opinion: running docker-setup.sh and calling it done teaches you nothing about what actually breaks in OpenClaw Docker deployments.

I spent two evenings debugging a “working” setup that mysteriously couldn’t connect the CLI to the gateway. Logs said the gateway was up. Port was open. Token matched. Still: ECONNREFUSED.

The problem? The CLI container wasn’t in the gateway’s network namespace. The setup script adds network_mode: "service:openclaw-gateway" automatically – but if you skip the script or customize anything, that line disappears. Nothing tells you why commands fail.

Why Manual Docker Config Beats the Setup Script

The official docker-setup.sh script handles onboarding, builds or pulls the image, writes your docker-compose.yml, and starts the gateway. Clean. Fast. But it hides three mismatches that silently break customized deployments:

  • CLI network isolation – The CLI won’t reach ws://127.0.0.1:18789 unless it shares the gateway’s network namespace.
  • Token override trap – Setting OPENCLAW_GATEWAY_TOKEN in your .env file silently overrides the token in openclaw.json. Auth fails even when your config file is correct.
  • UID mismatch – The container runs as user node (UID 1000). If your host directories aren’t owned by UID 1000, you get permission denied errors that Docker logs don’t explain clearly.

Building the compose file yourself forces you to confront these before they break at 2 AM.

Setup Script vs. Manual: What You Actually Trade

Use the script for throwaway testing. Build the compose file manually for production, home servers, or CI integration.

Aspect docker-setup.sh Manual docker-compose.yml
Time to first run 5 minutes 15 minutes
Understanding None – script does it all You see every config decision
Customization Edit afterward, risk breaking hidden dependencies Choose defaults upfront
Debugging Opaque – what did the script write? You wrote it, you know what’s there
Updates Re-run script, hope it merges cleanly Edit compose file, control exactly what changes

Manual Deployment Walkthrough

This assumes Docker and Docker Compose are installed. Get Docker here if you don’t have it.

Step 1: Create the Directory Structure

mkdir -p ~/.openclaw ~/openclaw/workspace
sudo chown -R 1000:1000 ~/.openclaw ~/openclaw/workspace

The container runs as UID 1000 (per the official Docker docs). Skip the chown? You’ll hit EACCES: permission denied when OpenClaw tries to write config files.

Step 2: Write docker-compose.yml

version: '3.8'

services:
 openclaw-gateway:
 image: ghcr.io/openclaw/openclaw:latest
 container_name: openclaw-gateway
 restart: unless-stopped
 ports:
 - "18789:18789"
 volumes:
 - ~/.openclaw:/home/node/.openclaw
 - ~/openclaw/workspace:/home/node/.openclaw/workspace
 environment:
 - NODE_ENV=production
 - TZ=UTC
 command: [
 "node",
 "dist/index.js",
 "gateway",
 "--bind", "lan",
 "--port", "18789"
 ]

 openclaw-cli:
 image: ghcr.io/openclaw/openclaw:latest
 network_mode: "service:openclaw-gateway"
 volumes:
 - ~/.openclaw:/home/node/.openclaw
 - ~/openclaw/workspace:/home/node/.openclaw/workspace
 profiles:
 - tools

Three lines that matter:

network_mode: "service:openclaw-gateway" on the CLI. Without it? The CLI tries ws://127.0.0.1:18789 and gets ECONNREFUSED because it’s in a separate network namespace.

--bind lan lets host browsers reach the gateway. Don’t use --bind 0.0.0.0. The CLI rejects it – as of version 2026.3.8, confirmed in GitHub Issue #44101.

profiles: [tools] on CLI prevents the CLI container from starting automatically. You run it on-demand with docker compose run.

Step 3: Start the Gateway

docker compose up -d openclaw-gateway
docker compose logs -f openclaw-gateway

Wait for [gateway] listening on ws://0.0.0.0:18789 in the logs.

Step 4: Run Onboarding

docker compose run --rm openclaw-gateway 
 node dist/index.js onboard --mode local --no-install-daemon

This runs interactively. It’ll ask for model provider (Anthropic, OpenAI, OpenRouter). As of April 4, 2026, Anthropic shut down OAuth for third-party tools – you’ll need an API key, not subscription credits. Paste your API key. It saves to ~/.openclaw/openclaw.json. The gateway token auto-generates. That’s what you’ll use to auth the dashboard.

Pro tip: OpenAI with OAuth? The wizard opens a browser URL. In headless Docker, the browser won’t launch – copy the redirect URL manually and paste it back into the terminal (official Docker guide covers this).

Step 5: Access the Dashboard

docker compose run --rm --profile tools openclaw-cli dashboard --no-open

Copy the URL it prints (something like http://127.0.0.1:18789/?token=abc123...). Open it in your browser. You’re in.

The Three Gotchas That Break Silently

1. CLI Can’t Reach Gateway (ECONNREFUSED)

Symptom: docker compose run openclaw-cli devices list fails with connect ECONNREFUSED 127.0.0.1:18789.

The CLI defaults to ws://127.0.0.1:18789. But unless you add network_mode: "service:openclaw-gateway", it’s in a separate network namespace where 127.0.0.1 means the CLI container’s own loopback – not the gateway’s.

Fix: Add that line to docker-compose.yml under openclaw-cli. (Issue #5559 documents this – the manual Docker setup instructions originally omitted it.)

2. Token Mismatch Even When Config Looks Right

Dashboard shows “unauthorized: gateway token mismatch.” You check ~/.openclaw/openclaw.json – the token matches what you copied. Still fails.

Set OPENCLAW_GATEWAY_TOKEN in your .env file or as an environment variable in docker-compose.yml? It silently overrides gateway.auth.token from the config file. The gateway uses the env var, the CLI uses the config file – mismatch.

Fix: Remove OPENCLAW_GATEWAY_TOKEN from your env entirely, or make sure it matches the token in openclaw.json. (Issue #9028 documents this.)

3. Permission Denied on /home/node/.openclaw

Symptom: Error: EACCES: permission denied, mkdir '/home/node/.openclaw/identity'

The container runs as user node (UID 1000). Your host directories are probably owned by your user (UID 1001, 501, etc.). UID mismatch – container can’t write.

Fix:

sudo chown -R 1000:1000 ~/.openclaw ~/openclaw/workspace

Do this before first run. Already started the container? Stop it, fix ownership, restart. This is the #1 issue in GitHub reports (#5434, #23948).

Connecting a Messaging Channel

Telegram is the easiest to test. Create a bot via @BotFather, get the token, then:

docker compose run --rm --profile tools openclaw-cli 
 channels add --channel telegram --token "YOUR_BOT_TOKEN"

OpenClaw sends a pairing code to your Telegram. Approve it:

docker compose run --rm --profile tools openclaw-cli 
 pairing approve telegram YOUR_CODE

Now message your bot. It replies via the gateway.

WhatsApp, Slack, Discord – same pattern. Add the channel, approve pairing. The official channel docs cover each platform.

What About Local Models?

Zero API costs? Docker released Docker Model Runner integration in February 2026. Pull a model with docker model pull, point OpenClaw at localhost:12434, done. Fully local. No telemetry.

The catch: these models eat RAM. Docker’s blog notes that “over a quarter of all production code is now AI-authored” (as of March 2026) – but that doesn’t mean your 2GB VPS can run a 7B model smoothly. Budget 8GB+ for anything serious.

Your Next Move

You’ve got a working gateway. CLI connects. Dashboard is up. Telegram bot responds.

Install a skill. The dashboard has a Skills panel – search for “calendar,” “github,” “notion,” whatever you use. Click install. Your agent can now interact with those services. Or connect a second channel. Or set up a cron job to summarize your inbox every morning.

The compose file you built gives you full control to add, remove, or tweak any part of the stack without re-running a script that might overwrite your changes. That’s the real win: you understand what’s running, so you can fix it when it breaks.

FAQ

Can I use docker run instead of docker compose?

Yes, but you lose the CLI container’s network_mode trick. Stick with Compose.

My gateway keeps restarting with exit code 137 – what’s wrong?

Exit 137 is an OOM kill. The Linux kernel terminated the process because it ran out of memory. Building the image locally (not using the pre-built ghcr.io/openclaw/openclaw:latest)? pnpm install needs at least 2 GB RAM. On a 1 GB VPS, it’ll silently fail. I ran into this on a DigitalOcean droplet – upgraded to 2GB, worked fine. Use the pre-built image, or upgrade your VPS (per the official docs and confirmed by the QubitTool deployment guide).

How do I update OpenClaw to the latest version?

docker compose pull openclaw-gateway
docker compose up -d openclaw-gateway
docker image prune -f

Your config and workspace are in mounted volumes, so they survive. The prune cleans up the old image. Latest version as of this writing is 2026.4.11, released April 2026.