Install and configure a fully operational Dockerized OpenClaw instance on macOS from scratch. Includes browser pairing, Discord channel setup, and optional Gmail/Google Drive integration. Use when user asks to "install openclaw docker", "set up dockerized openclaw", "openclaw in docker", or "isolated openclaw instance".
Install a fully isolated, production-ready OpenClaw instance inside Docker on macOS.
One session, zero to running. All common pitfalls are handled inline.
Supports multiple instances on the same machine. Each instance gets a unique name and port.
What you end up with:
A named container running and auto-restarting
Persistent data via named Docker volumes (survives container recreation)
(Optional) Gmail working via Himalaya (supports attachments); Google Drive via gog
Step 0: Pick Your Instance Name and Port
Run this auto-detect script. It scans existing OpenClaw containers, finds the next free port, and suggests a name. Confirm or override.
bash
# Auto-detect existing instances and suggest next available name+port
python3 - << 'AUTODETECT'
import subprocess, re
# Find all running openclaw containers
result = subprocess.run(
["docker", "ps", "-a", "--format", "{{.Names}} {{.Ports}}"],
capture_output=True, text=True
)
existing = {}
for line in result.stdout.strip().splitlines():
parts = line.split(' ')
name = parts[0]
ports = parts[1] if len(parts) > 1 else ""
if 'openclaw' in name.lower() or '18789' in ports:
m = re.search(r'0\.0\.0\.0:(\d+)->18789', ports)
port = int(m.group(1)) if m else None
existing[name] = port
# Find next free port starting from 19002
used_ports = set(p for p in existing.values() if p)
port = 19002
while port in used_ports:
port += 1
# Suggest name
count = len(existing) + 1
names = ["openclaw-main", "openclaw-work", "openclaw-demo", "openclaw-test", "openclaw-lab"]
suggested_name = names[min(count - 1, len(names) - 1)]
print("\n=== Existing OpenClaw instances ===")
if existing:
for n, p in existing.items():
print(f" {n} → port {p}")
else:
print(" (none found)")
print(f"\n=== Suggested for new instance ===")
print(f" INSTANCE={suggested_name}")
print(f" HOST_PORT={port}")
print(f"\nTo accept, run:")
print(f" export INSTANCE={suggested_name}")
print(f" export HOST_PORT={port}")
print(f"\nTo override, replace the values and run the export commands with your chosen values.")
AUTODETECT
Review the output, then set your variables:
bash
# Accept suggestion (paste the export lines from the output above)
export INSTANCE=openclaw-main
export HOST_PORT=19002
# Or override with your own values:
export INSTANCE=openclaw-demo
export HOST_PORT=19003
All subsequent commands in this guide use $INSTANCE and $HOST_PORT. Keep this terminal session open, or re-export the variables if you open a new one.
Multiple instances example:
Instance
Host port
Purpose
openclaw-main
19002
Primary personal assistant
openclaw-demo
19003
Public demo / lecture
openclaw-work
19004
Work projects
Each instance has its own volumes ($INSTANCE-data, $INSTANCE-home) — data is fully isolated.
Prerequisites
macOS (Darwin) — Intel or Apple Silicon
Docker Desktop installed and running (or Docker Engine + CLI)
Claude Code CLI installed on the host if using a Claude Max/Pro subscription (claude command available). Skip if using a raw API key.
Verify Docker is running:
bash
docker --version
docker ps
Both commands must succeed before continuing.
Step 1: Pull the Image
The official image is on GitHub Container Registry (GHCR), not Docker Hub.
bash
docker pull ghcr.io/openclaw/openclaw:latest
Pitfall:openclaw/openclaw on Docker Hub does not exist. Always use ghcr.io/openclaw/openclaw:latest.
Success: Pull completes without error and docker images | grep openclaw shows the image.
Step 2: Generate a Claude Setup Token
Skip this step if you have a raw Anthropic API key — you will pass it via -e ANTHROPIC_API_KEY=sk-ant-api03-... in Step 3 instead.
If you have a Claude Max or Pro subscription, generate a setup token on the host:
bash
claude setup-token
Copy the token (format: sk-ant-oat01-...). You will paste it into the container in Step 5.
Pitfall: This is a setup token, not an API key. The two are different. A setup token lets the container authenticate using your subscription. An API key charges per token.
Step 3: Launch the Container
Use the $INSTANCE and $HOST_PORT variables you set in Step 0. Do not reduce the memory — 512 MB and 1024 MB are insufficient and cause crash loops.
If using a raw API key instead of a setup token, add -e ANTHROPIC_API_KEY=sk-ant-api03-... before the image name.
Wait 10 seconds, then verify:
bash
docker ps --filter name=$INSTANCE
Success: Status shows Up X seconds and the container is not restarting.
Pitfall — OOM crash loop: If the container keeps restarting, check logs:
docker logs --tail 20 $INSTANCE
If you see JavaScript heap out of memory, the container needs -m 2048m AND -e NODE_OPTIONS="--max-old-space-size=1024". Recreate with the full command above.
Pitfall — port conflict: If the port is in use, you chose the wrong HOST_PORT in Step 0. Re-run the conflict check: lsof -i :$HOST_PORT. Pick a free port and relaunch.
Step 4: Configure the Gateway
The gateway binds to 127.0.0.1 (loopback) inside the container by default. Docker port-forwarding sends traffic to the container's network interface, not its loopback. You must switch to LAN mode.
4a. Set bind to LAN
bash
docker exec $INSTANCE node /app/openclaw.mjs config set gateway.bind lan
4b. Set allowed origins
Non-loopback bind requires explicitly allowed origins or the gateway refuses to start:
Pitfall — crash after setting LAN bind: If you set gateway.bind lan but forget the allowedOrigins step, the container crash-loops with non-loopback Control UI requires gateway.controlUi.allowedOrigins. Run step 4b, then restart.
Step 5: Register Authentication
Important: Do NOT paste your token directly in the command line — it would be stored in shell history. The command below prompts interactively.
Pitfall — openclaw command not found: The CLI binary is NOT installed as a global command in this image. Always use node /app/openclaw.mjs inside the container:
Copy the generated URL, open it in a browser, and add the bot to your server
Pitfall — slash commands say "not authorized": If you invite the bot without applications.commands, slash commands will not work. Re-authorize using this URL (replace BOT_CLIENT_ID):
Pitfall — adding users later:config set guilds replaces the entire object. Always include ALL existing user IDs when adding new ones.
Optional: Gmail Integration
Skip this section if you do not need Gmail. The rest of the setup works without it.
See references/gmail-setup.md for the complete guide (uses Himalaya — the only reliable option for attachment download).
The key insight: OAuth browser auth does NOT work inside Docker because the callback URL points to localhost inside the container, which the host browser cannot reach. The solution is to authenticate on the host Mac first, then copy tokens into the container.
Optional: Google Drive Integration
Skip this section if you do not need Google Drive. Google Drive uses gog (gogcli) — independent of the Gmail/Himalaya setup. You can set up Drive without setting up Gmail.
See references/google-drive-setup.md for the complete guide.
Short answer: Yes for initial setup. No for ongoing use.
Why initial setup requires your Mac
Three steps require direct Mac access (physical or SSH with port forwarding):
Step
Why Mac access is needed
Step 2: Claude setup token
claude setup-token opens a browser auth flow on localhost. Cannot be done remotely without a browser on the Mac. Skip if using a raw API key — that can be set from anywhere.
Step 4: Browser pairing
The OpenClaw dashboard runs at http://127.0.0.1:$HOST_PORT/ — only reachable from the Mac. You must open a browser there to pair.
Optional Gmail/Drive OAuth
Google OAuth callback points to localhost on the Mac. Must authenticate from a browser running on the Mac.
Remote setup is possible via SSH port forwarding
If you SSH into your Mac from another machine, you can forward the container port to your local browser:
bash
# From your remote machine — forward Mac's port 19002 to your local 19002
ssh -L 19002:localhost:19002 your-mac.local
# Then open http://127.0.0.1:19002/ in your local browser
This lets you complete the browser pairing step remotely.
After setup is complete
Once the container is running and paired, you do not need to be at your Mac. OpenClaw runs as a background service (--restart unless-stopped). You interact with it entirely through your configured channel (Discord, Telegram, etc.) — from your phone, any browser, anywhere.
Configuration
No persistent configuration required. All settings are chosen interactively in Step 0 and set as shell variables ($INSTANCE, $HOST_PORT).