Two ways to deploy OpenClaw in Docker: run docker-setup.sh and be online in 5 minutes, or write your own docker-compose.yml and control every variable. The first works until you need CLI commands or non-localhost access. The second requires understanding what the script actually does.
The trade-off. Script handles volumes, env vars, onboarding, starts the gateway automatically. But – it makes network assumptions that break in Docker Desktop on Windows, VPS deployments behind reverse proxies, anywhere the CLI container needs to talk to the gateway container.
What the Official Script Does (And Hides)
The official docker-setup.sh script clones the repo, builds a local image, creates two directories (~/.openclaw for config, ~/openclaw/workspace for agent files), runs an onboarding wizard for API keys, launches the gateway via Compose.
What it doesn’t tell you: openclaw-cli container doesn’t share the gateway’s network namespace by default. CLI commands try ws://127.0.0.1:18789 – in Docker that’s the CLI container’s own loopback, not the gateway. Connection fails with ECONNREFUSED. GitHub Issue #11456 documents this.
Fix is in the issue thread, not the setup guide: add network_mode: "service:openclaw-gateway" to openclaw-cli service. The script does write this to docker-compose.extra.yml – but only if you set certain env vars first. Run the script without reading source? You won’t know the file exists.
Running the Official Setup
Clone and run:
git clone https://github.com/openclaw/openclaw.git
cd openclaw
./scripts/docker/setup.sh
Wizard asks: Gateway mode (choose local unless deploying to remote server), Network bind (lan for host browser access, loopback for container-only), LLM provider (Anthropic, OpenAI, Google, local Ollama – wizard writes API key to .env).
Wait 40-60 seconds. Gateway takes time. You’ll see [entrypoint] Starting OpenClaw gateway first – not the ready signal. Wait for [gateway] listening on ws://127.0.0.1:18789.
Open http://localhost:18789. Authentication error. Gateway requires a token:
docker compose run --rm openclaw-cli dashboard --no-open
Copy the URL with ?token=..., paste into browser.
The Docker Desktop Windows Trap
Dashboard loads but shows disconnected (1008): pairing required. You entered the token correctly. Still doesn’t work.
NAT. Browser connects to http://localhost:18789, Docker Desktop’s networking layer translates that to the container’s IP. Gateway sees the connection from 172.18.0.1 (Docker’s bridge network), not 127.0.0.1. OpenClaw treats non-localhost as external, requires device pairing – browser can’t complete.
Issue #4941 shows gateway logs: remote=172.18.0.1 code=1008 reason=pairing required. Two config changes in ~/.openclaw/openclaw.json:
{
"gateway": {
"auth": {
"mode": "token",
"token": "your-token-here"
},
"controlUi": {
"allowInsecureAuth": true
},
"trustedProxies": ["172.18.0.0/16"]
}
}
allowInsecureAuth: gateway accepts token auth from proxied connections. trustedProxies: trust requests from Docker’s bridge network range. Restart after editing.
Pro tip: Deploying behind a reverse proxy (Traefik, Caddy, Nginx)? Same issue. Add your proxy’s IP range to
trustedProxiesand setgateway.controlUi.allowedOriginsto include your public domain. Otherwise: “origin not allowed” errors when accessing from anything but localhost.
Manual Deployment: Full Control, More Config
Need to customize – run multiple instances, integrate with existing networks, VPS with specific volume mounts – write your own docker-compose.yml.
Minimal working config:
version: '3.8'
services:
openclaw-gateway:
image: ghcr.io/openclaw/openclaw:latest
container_name: openclaw
restart: unless-stopped
ports:
- "18789:18789"
volumes:
- ~/.openclaw:/home/node/.openclaw
- ~/openclaw/workspace:/home/node/.openclaw/workspace
environment:
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
- OPENCLAW_GATEWAY_TOKEN=${OPENCLAW_GATEWAY_TOKEN}
init: true
openclaw-cli:
image: ghcr.io/openclaw/openclaw:latest
network_mode: "service:openclaw-gateway"
volumes:
- ~/.openclaw:/home/node/.openclaw
profiles: ["cli"]
Create .env:
ANTHROPIC_API_KEY=sk-ant-...
OPENCLAW_GATEWAY_TOKEN=your-random-token
network_mode: "service:openclaw-gateway" – critical. Without it? CLI commands fail. profiles: ["cli"] prevents CLI container from auto-starting – you only need it when running commands.
Start gateway:
docker compose up -d openclaw-gateway
Run CLI:
docker compose run --rm openclaw-cli devices list
Config Conflicts: When Env Vars Override JSON
You set gateway.bind = "lan" in openclaw.json. Restart. Logs still show [gateway] listening on ws://127.0.0.1:18789. Gateway ignores your config.
This is Issue #43886. OpenClaw reads config in order: environment variables (highest), command-line flags, openclaw.json (lowest).
If docker-compose.yml sets OPENCLAW_GATEWAY_BIND=loopback or GATEWAY_MODE=local, those override JSON. Gateway binds to 127.0.0.1 only. External connections fail.
Check running container’s env vars:
docker exec openclaw env | grep OPENCLAW
See OPENCLAW_GATEWAY_BIND=loopback? Remove from docker-compose.yml, restart. Or keep the env var, delete the conflicting line from openclaw.json. Pick one source of truth.
Permission Errors on Linux
Official image runs as node user (uid 1000). Host directories not owned by uid 1000? Container can’t write to ~/.openclaw. EACCES errors in logs.
Fix:
sudo chown -R 1000:1000 ~/.openclaw ~/openclaw/workspace
Doesn’t come up on macOS/Windows – Docker Desktop handles UID mapping. Linux: set it manually.
Docker vs Other Deployment Methods
| Method | Pros | Cons |
|---|---|---|
| docker-setup.sh | Fast setup, handles onboarding, works on all platforms | CLI networking issues on some setups, hidden config in extra files, less control |
| Manual docker-compose | Full control, easy to version and replicate, explicit config | Requires Docker networking knowledge, more initial setup, manual onboarding |
| Direct npm install | No Docker overhead, direct file access, easier debugging | No isolation, requires Node 22+, PATH issues, can break system packages |
| Managed hosting | Zero config, handles updates, no server maintenance | Costs $45-200/month, less customization, data on third-party servers |
Docker sits in the middle. More isolated than direct install, more complex than managed hosting. For most developers? Right choice. You control your data, customize the setup, don’t manage Node.js versions or global npm packages.
What to Do Next
Gateway running, dashboard connects? Add a messaging channel. Telegram: fastest. Create bot via @BotFather, get API token:
docker compose run --rm openclaw-cli channels add --channel telegram --token "YOUR_TOKEN"
Send message to your bot. OpenClaw replies with pairing code. Approve:
docker compose run --rm openclaw-cli pairing approve telegram CODE
Control OpenClaw from your phone. Test: “what’s the current time” or “list files in the workspace.” Responds? Deployment works. Doesn’t? Check logs:
docker logs openclaw --tail 50
Look for API key errors, model selection issues, rate limit warnings. Most failures happen at the LLM provider layer, not Docker.
Actually – one thing nobody mentions. The gateway takes 40-60 seconds to start, but docker compose up reports “up” immediately. Send requests before [gateway] listening on appears? Connection refused. The [entrypoint] Starting OpenClaw gateway line lies. Wait for the real signal.
FAQ
Why does docker compose run --rm openclaw-cli fail with connection refused?
CLI container doesn’t share gateway’s network. Add network_mode: "service:openclaw-gateway" to openclaw-cli in docker-compose.yml or use docker-compose.extra.yml if the setup script made one.
Can I run OpenClaw on a Raspberry Pi?
Yes. Official image supports ARM64. Use ghcr.io/openclaw/openclaw:latest on Pi 4 with 4GB+ RAM. A Pi 4 draws ~5W – running 24/7 costs $5/year in electricity, cheaper than any VPS. Response times slower if using cloud models over residential internet, but it works. Turns out the bottleneck isn’t the Pi’s CPU – it’s API latency. Claude’s endpoint takes 2-4 seconds to respond regardless of where you call from. The Pi adds maybe 50ms of processing overhead. Your home upload speed matters more than the hardware.
How do I update OpenClaw after deploying with Docker?
Pull latest image, stop container, recreate. Config and workspace persist in mounted volumes – updates are non-destructive: docker compose pull && docker compose up -d. Gateway restarts with new version. Check logs to confirm success. Running a specific version tag (e.g., 2026.2.26) instead of latest? Update the tag in docker-compose.yml before pulling. One catch: if the new version changes config schema, you might need to migrate openclaw.json. The release notes usually mention this, but not always. Safe bet: back up ~/.openclaw before updating. Had one update that renamed gateway.auth.token to gateway.controlUi.authToken – old config broke silently until I checked the changelog.