openclaw-docker-setup

GitHub 作者 LeoYeAI/openclaw-master-skills

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".

安装 / 下载方式

TotalClaw CLI推荐
totalclaw install github:LeoYeAI~openclaw-master-skills~openclaw-docker-setup
cURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/github%3ALeoYeAI~openclaw-master-skills~openclaw-docker-setup/file -o openclaw-docker-setup.md
# openclaw-docker-setup

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)
- Dashboard accessible at http://127.0.0.1:YOUR_PORT/
- Discord channel configured and responding
- (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.

```bash
docker run -d \
  --name $INSTANCE \
  --restart unless-stopped \
  -p $HOST_PORT:18789 \
  -m 2048m \
  --cpus=2 \
  --cap-drop=ALL \
  --cap-add=NET_BIND_SERVICE \
  --security-opt no-new-privileges \
  -v ${INSTANCE}-data:/app/data \
  -v ${INSTANCE}-home:/home/node \
  -e NODE_OPTIONS="--max-old-space-size=1024" \
  ghcr.io/openclaw/openclaw:latest
```

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:

```bash
docker exec $INSTANCE node /app/openclaw.mjs config set \
  gateway.controlUi.allowedOrigins '["http://127.0.0.1:$HOST_PORT"]' --json
```

### 4c. Restart to apply

```bash
docker restart $INSTANCE
```

Wait 10 seconds, then verify:

```bash
curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:$HOST_PORT/
```

**Success:** Returns `200`.

> **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.

```bash
docker exec -it $INSTANCE node /app/openclaw.mjs models auth paste-token --provider anthropic
```

When prompted, paste the setup token from Step 2 (or your API key if using one).

**Success:**
```
Updated ~/.openclaw/openclaw.json
Auth profile: anthropic:manual (anthropic/token)
```

> **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:
> ```bash
> docker exec $INSTANCE node /app/openclaw.mjs <command>
> ```
> For interactive commands (pasting tokens, TTY prompts), add `-it`:
> ```bash
> docker exec -it $INSTANCE node /app/openclaw.mjs <command>
> ```

---

## Step 6: Verify Authentication and Gateway

```bash
# Check model auth
docker exec $INSTANCE node /app/openclaw.mjs models status
```

**Success:** Output includes `Providers w/ OAuth/tokens (1): anthropic (1)`

```bash
# Check gateway
docker exec $INSTANCE node /app/openclaw.mjs gateway status
```

**Success:** Output includes `RPC probe: ok`

---

## Step 7: