← Back to Blog
EN中文

Docker 部署 OpenClaw:从踩坑到跑通的完整记录

本文记录在 macOS Docker Desktop 环境下部署 OpenClaw Agent 的全过程,包括 Tailscale Sidecar 组网、Gateway 配置、Dashboard 连接等常见问题的解决方案。

背景

OpenClaw 是 2025 年底开源的 AI Agent 框架,在 GitHub 上已超过 145k stars。它提供了 Gateway(常驻守护进程)+ CLI(按需交互)的架构,支持多模型接入、Heartbeat 定时任务、Memory 持久化等企业级特性。

官方提供了 alpine/openclaw:latest Docker 镜像,但实际部署中会遇到不少文档未充分覆盖的问题。本文是一份"踩完所有坑之后"的实战指南。

环境

  • macOS + Docker Desktop
  • Tailscale 用于访问内网服务(如自托管 Gitea)
  • OpenAI API(gpt-4o)

架构设计

┌─────────────────────────────────────────────┐
│  Docker Compose                             │
│                                             │
│  ┌───────────┐    network_mode:service      │
│  │ Tailscale │◄────────────────────────┐    │
│  │ Sidecar   │    共享网络命名空间      │    │
│  └─────┬─────┘                         │    │
│        │ ports: 18790:18790            │    │
│        │                         ┌─────┴──┐ │
│        │                         │OpenClaw│ │
│        │                         │Gateway │ │
│        │                         └────────┘ │
│        │                                    │
│  ┌─────┴──────┐                             │
│  │ OpenClaw   │  profiles: [cli]            │
│  │ CLI        │  按需启动                    │
│  └────────────┘                             │
└─────────────────────────────────────────────┘

关键决策:Gateway 通过 network_mode: "service:tailscale" 共享 Tailscale 容器的网络栈,从而直接访问 Tailnet 内的服务。

docker-compose.yml

services:
  tailscale:
    image: tailscale/tailscale:latest
    container_name: my-ts
    hostname: my-agent
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
      - NET_RAW
    devices:
      - /dev/net/tun:/dev/net/tun
    volumes:
      - ts-state:/var/lib/tailscale
    env_file:
      - ./secrets/.env
    environment:
      - TS_STATE_DIR=/var/lib/tailscale
      - TS_USERSPACE=false
      - TS_ACCEPT_DNS=true
    ports:
      - "18790:18790"
    extra_hosts:
      - "my-server:100.x.x.x"  # Tailscale IP 硬编码

  openclaw-gateway:
    image: alpine/openclaw:latest
    container_name: my-gateway
    restart: unless-stopped
    depends_on:
      - tailscale
    network_mode: "service:tailscale"
    env_file:
      - ./secrets/.env
    environment:
      HOME: /home/node
      TERM: xterm-256color
    volumes:
      - openclaw-config:/home/node/.openclaw
      - openclaw-workspace:/home/node/.openclaw/workspace
    init: true
    security_opt:
      - no-new-privileges:true
    command:
      ["node", "dist/index.js", "gateway", "--bind", "lan", "--port", "18790"]

  openclaw-cli:
    image: alpine/openclaw:latest
    container_name: my-cli
    env_file:
      - ./secrets/.env
    environment:
      HOME: /home/node
      TERM: xterm-256color
      BROWSER: echo
    volumes:
      - openclaw-config:/home/node/.openclaw
      - openclaw-workspace:/home/node/.openclaw/workspace
    stdin_open: true
    tty: true
    init: true
    entrypoint: ["node", "dist/index.js"]
    profiles:
      - cli

volumes:
  ts-state:
  openclaw-config:
  openclaw-workspace:

踩坑记录

坑 1:镜像名和入口点

Docker Hub 上的官方镜像是 alpine/openclaw,不是 openclaw/openclaw

容器内没有独立的 openclaw 二进制文件。这是一个 Node.js 应用,入口点是:

node dist/index.js          # CLI 模式
node dist/index.js gateway   # Gateway 模式

参考 官方 docker-compose.yml 可以确认这一点。

坑 2:文件权限(EACCES)

Docker named volume 首次创建时目录属于 root,但 OpenClaw 以 node 用户(uid 1000)运行:

EACCES: permission denied, open '/home/node/.openclaw/.env'

解决方案:

docker compose run --rm -u root --entrypoint sh openclaw-cli \
  -c "chown -R node:node /home/node/.openclaw"

注意:必须 override entrypoint,否则默认会执行 node dist/index.js

坑 3:Tailscale DNS 不生效

即使设置了 TS_ACCEPT_DNS=true,容器内仍无法解析 Tailscale 主机名:

curl: (6) Could not resolve host: my-server

根因: macOS Docker Desktop 的 /dev/net/tun 支持不完整,Tailscale kernel 模式下的 DNS 拦截可能不生效。

解决方案: 在 docker-compose.yml 中用 extra_hosts 硬编码 Tailscale IP:

extra_hosts:
  - "my-server:100.x.x.x"

IP 地址通过 tailscale status 获取。虽然不够优雅,但 100% 可靠。

坑 4:Gateway 启动被阻止

Gateway start blocked: set gateway.mode=local (current: unset) or pass --allow-unconfigured.

解决方案:openclaw.json 中显式声明:

{
  "gateway": {
    "mode": "local"
  }
}

坑 5:配置格式已迁移

如果你参考的是旧版文档,可能会写出这样的配置:

{
  "agent": {
    "model": "openai/o3"
  }
}

OpenClaw 2026.2.x 版本会报错:

agent.* was moved; use agents.defaults instead
agent.model string was replaced by agents.defaults.model.primary/fallbacks

正确格式:

{
  "agents": {
    "defaults": {
      "model": {
        "primary": "openai/gpt-4o",
        "fallbacks": ["openai/gpt-4o-mini"]
      }
    }
  }
}

此外,compaction.mode: "aggressive" 也不是合法值,直接删掉让它用默认值即可。

坑 6:--bind 参数

Gateway 的 --bind 只接受以下关键字:

含义
loopback 127.0.0.1,仅本地
lan 0.0.0.0,Docker 环境必选
tailnet 绑定 Tailscale 接口
auto 自动选择
custom 指定具体 IP

不能传 0.0.0.0,必须用 lan

坑 7:Dashboard "pairing required"(1008)

这是最折腾的一个问题。Dashboard 页面能打开,但 WebSocket 连接立刻断开:

disconnected (1008): pairing required

根因: 浏览器通过 localhost:18790 访问,但请求经过 Docker NAT + Tailscale 网络转发后,Gateway 看到的 remote IP 不是 127.0.0.1(日志中显示为 Tailscale DERP 中继 IP 172.105.x.x)。Gateway 将其视为外部连接,触发设备配对校验。

尝试过但无效的方案:

  • trustedProxies: ["0.0.0.0/0"] — 只控制是否信任代理头,不绕过 pairing

最终解决方案:openclaw.json 中添加:

{
  "gateway": {
    "controlUi": {
      "enabled": true,
      "allowInsecureAuth": true
    }
  }
}

allowInsecureAuth: true 让 Dashboard 在 HTTP 下接受 token-only 认证,绕过设备身份验证和 pairing 检查。

坑 8:端口冲突

如果宿主机上已有另一个 OpenClaw 实例占用了默认端口 18789,Gateway 会启动失败。改用其他端口(如 18790),注意同步修改:

  1. docker-compose.yml 的 command --port 参数
  2. docker-compose.yml 的 tailscale ports 映射
  3. openclaw.jsongateway.port

三处必须一致。

Onboarding 流程

首次运行需要 onboarding 向导完成初始化:

# 先停 gateway,避免未初始化时反复重启
docker compose stop openclaw-gateway

# 运行 onboarding
docker compose run --rm openclaw-cli onboard

向导会依次询问:

步骤 建议
Security acknowledgment 确认
QuickStart / Advanced QuickStart
Model provider OpenAI
Default model 按需选择(gpt-4o 稳定,o3 需组织验证)
Channel(WhatsApp/Telegram 等) Skip
Skill dependencies(各种 API Key) 全部 No
Hooks Skip for now

完成后启动 Gateway:

docker compose up -d

获取 Dashboard 地址:

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

最终的 openclaw.json

{
  "gateway": {
    "mode": "local",
    "bind": "lan",
    "port": 18790,
    "trustedProxies": ["0.0.0.0/0"],
    "controlUi": {
      "enabled": true,
      "allowInsecureAuth": true
    }
  },
  "agents": {
    "defaults": {
      "model": {
        "primary": "openai/gpt-4o",
        "fallbacks": ["openai/gpt-4o-mini"]
      },
      "heartbeat": {
        "every": "24h",
        "model": "openai/gpt-4o-mini",
        "prompt": "Check workspace for pending tasks. Execute if needed, otherwise reply HEARTBEAT_OK."
      }
    }
  }
}

容器内 SSH 配置(访问 Git 服务)

如果需要让 Agent 推送代码到自托管 Git 服务:

# 在容器内生成密钥
docker compose exec openclaw-gateway \
  ssh-keygen -t ed25519 -C "openclaw@agent" -f /home/node/.ssh/id_ed25519 -N ""

# 查看公钥,添加到 Git 服务
docker compose exec openclaw-gateway cat /home/node/.ssh/id_ed25519.pub

# 配置 SSH(以 Gitea 端口 2222 为例)
docker compose exec openclaw-gateway bash -c 'cat >> /home/node/.ssh/config << EOF
Host my-server
  Port 2222
  User git
  IdentityFile /home/node/.ssh/id_ed25519
EOF'

# 配置 git 用户信息
docker compose exec openclaw-gateway bash -c \
  'git config --global user.name "openclaw" && git config --global user.email "[email protected]"'

注意:SSH 密钥存在容器文件系统中,不在 named volume 里。容器重建后需要重新生成。如果需要持久化,可以挂载额外 volume 到 /home/node/.ssh

数据持久化

Volume 容器路径 内容
ts-state /var/lib/tailscale Tailscale 节点状态
openclaw-config /home/node/.openclaw Agent 配置、会话、记忆
openclaw-workspace /home/node/.openclaw/workspace 工作区文件

配置文件(openclaw.jsonSOUL.md 等)从宿主机注入:

docker compose cp config/openclaw.json openclaw-gateway:/home/node/.openclaw/openclaw.json
docker compose cp config/SOUL.md openclaw-gateway:/home/node/.openclaw/workspace/SOUL.md

日常操作速查

# 启动/停止
docker compose up -d
docker compose down

# 进入容器
docker compose exec openclaw-gateway bash          # node 用户
docker compose exec -u root openclaw-gateway bash  # root 用户

# CLI 操作
docker compose run --rm openclaw-cli status
docker compose run --rm openclaw-cli dashboard --no-open

# 查看日志
docker compose logs -f openclaw-gateway

# 复制配置到容器
docker compose cp config/openclaw.json openclaw-gateway:/home/node/.openclaw/openclaw.json
docker compose restart openclaw-gateway

总结

Docker 部署 OpenClaw 的核心难点不在 Agent 本身,而在于网络和认证:

  1. Tailscale Sidecar 模式下 DNS 不可靠 — 用 extra_hosts 硬编码解决
  2. Docker NAT 导致 Dashboard pairing 失败 — 用 controlUi.allowInsecureAuth 解决
  3. 配置格式迁移 — 关注 agent.*agents.defaults.* 的变化
  4. 入口点不是二进制 — 记住用 node dist/index.js

整个过程中最重要的经验是:不要过度工程化。先用官方镜像 + 最小配置跑通,确认每一步可用后再增量添加功能。试图一次性把所有配置、工具链、自动化脚本都搭好,只会让排错变得更困难。