$ ssh clawdbot.space --loading...
$ ssh clawdbot.space --loading...
在 Docker 容器中运行 OpenClaw — 从首次拉取到生产级 Compose 编排。
Docker 是在生产环境部署 OpenClaw 的推荐方式。它提供隔离性、可重复性和轻松更新的能力。本指南将引导你从基础单容器设置到完整的多服务 Compose 编排,包含监控、备份和自动重启。
docker pull ghcr.io/openclaw/openclaw:latest
mkdir -p ~/.openclaw && cd ~/.openclaw
docker run -d --name openclaw \ -p 127.0.0.1:18789:18789 \ -v ~/.openclaw:/app/data \ -e ANTHROPIC_API_KEY=sk-ant-xxx \ --restart unless-stopped \ ghcr.io/openclaw/openclaw:latest
docker logs -f openclaw
生产环境使用 Docker Compose,配置资源限制、健康检查和日志管理。
version: "3.8"
services:
openclaw:
image: ghcr.io/openclaw/openclaw:latest
container_name: openclaw
restart: unless-stopped
ports:
- "127.0.0.1:18789:18789"
volumes:
- ./data:/app/data
- ./config:/app/config:ro
environment:
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
- OPENCLAW_TZ=Asia/Shanghai
- OPENCLAW_LOG_LEVEL=info
deploy:
resources:
limits:
memory: 4g
cpus: "2.0"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:18789/health"]
interval: 30s
timeout: 10s
retries: 3
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"添加 --read-only 标志。挂载 /tmp 为 tmpfs 用于临时文件。
--read-only --tmpfs /tmp:rw,noexec,nosuid移除容器不需要的所有 Linux 权限。
--cap-drop=ALL在容器内以非 root 身份运行。
--user 1000:1000只绑定到 localhost。永远不要将 18789 暴露到 0.0.0.0。
-p 127.0.0.1:18789:18789始终将 /app/data 挂载到宿主机目录。这包含 SOUL.md、记忆和所有代理状态。
使用 :ro 标志挂载配置文件,防止代理修改自身配置。
使用 docker cp 或卷快照。用 cron 自动化:tar czf backup-$(date +%F).tar.gz ./data
把网关容器化,本质上是在决定哪些状态活在容器之外。这件事做错,症状是延迟出现的:一切正常,直到第一次重建镜像——那一刻 Agent 忘了自己是谁。
| 宿主机路径 | 挂载到 | 不挂会丢什么 |
|---|---|---|
| $HOME/.openclaw | /home/node/.openclaw | 配置和网关状态。每一次配对、每一项设置,重建即失。 |
| $HOME/.openclaw/workspace | /home/node/.openclaw/workspace | Agent 的工作文件,以及你写的所有技能。 |
| $HOME/.openclaw-auth-profile-secrets | /home/node/.config/openclaw | 恢复密钥。这个是无法重新生成的——当成 SSH 私钥来对待。 |
文档里有一条约束值得原样引用,因为它很容易搞错、而且失败得很难懂:网关状态要挂成目录,绝不要挂成单个文件。把单个 JSON 文件 bind-mount 进去,在某个东西以原子方式重写它之前都是好的——之后容器握着的是一个过期的 inode,你的修改看起来就凭空消失了。
仓库自带了 compose 文件和 setup 脚本,所以真正需要做的功课在它们周围的环境变量上,而不在自己写一份 compose 定义。
# The three paths the container expects on the host: export OPENCLAW_CONFIG_DIR="$HOME/.openclaw" export OPENCLAW_WORKSPACE_DIR="$HOME/.openclaw/workspace" export OPENCLAW_AUTH_PROFILE_SECRET_DIR="$HOME/.openclaw-auth-profile-secrets" # Use the prebuilt image rather than building locally: export OPENCLAW_IMAGE="ghcr.io/openclaw/openclaw:latest" # lan is the normal setting; loopback restricts access further: export OPENCLAW_GATEWAY_BIND=lan
除非有理由本地构建,否则直接用预构建镜像——本地构建会把一整套工具链拉到宿主机上,而你本来并不需要它。`OPENCLAW_GATEWAY_BIND=lan` 是常规取值;loopback 限制得更严,如果你只打算通过隧道访问,它是个合理的默认值。
./scripts/docker/setup.sh
这是受支持的路径。自己手写 compose 当然可以,代价是上面那套挂载语义(包括「文件还是目录」这个坑)从此归你自己负责。
docker compose ps docker compose logs --tail=100 openclaw-gateway curl -fsS http://127.0.0.1:18789/healthz
`ps` 告诉你容器起来了,`logs` 告诉你它启动过程干净,healthz 端点告诉你它真的在提供服务。一个容器完全可能通过前两项而在第三项失败——那正是「看起来没事、实际有事」的状态。
管理操作通过第二个短命容器完成,而不是 exec 进正在运行的那个。这样既不打扰网关进程,又保证你用的 CLI 和网关是同一个版本。
docker compose run --rm openclaw-cli dashboard --no-open
`--no-open` 在无头主机上很重要,否则 CLI 会尝试去启动一个根本不存在的浏览器。
docker compose run --rm openclaw-cli devices list docker compose run --rm openclaw-cli devices approve <requestId>
新设备需要批准之后才能和网关通信。如果出现了一个你不认得的请求,那是一起安全事件,不是一个麻烦。
docker compose run --rm openclaw-cli doctor --json
这里值得加 `--json`——容器日志是交错输出的,结构化输出比翻滚屏好搜索得多。
# Rebuilding without going through onboarding again: export OPENCLAW_SKIP_ONBOARDING=1 docker compose up -d --build
`OPENCLAW_SKIP_ONBOARDING=1` 会阻止向导再跑一遍、覆盖掉你后来改过的配置。这个参数通常是在丢过一次配置之后才被人发现的。