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),注意同步修改:
docker-compose.yml的 command--port参数docker-compose.yml的 tailscaleports映射openclaw.json的gateway.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.json、SOUL.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 本身,而在于网络和认证:
- Tailscale Sidecar 模式下 DNS 不可靠 — 用
extra_hosts硬编码解决 - Docker NAT 导致 Dashboard pairing 失败 — 用
controlUi.allowInsecureAuth解决 - 配置格式迁移 — 关注
agent.*→agents.defaults.*的变化 - 入口点不是二进制 — 记住用
node dist/index.js
整个过程中最重要的经验是:不要过度工程化。先用官方镜像 + 最小配置跑通,确认每一步可用后再增量添加功能。试图一次性把所有配置、工具链、自动化脚本都搭好,只会让排错变得更困难。