OpenClaw 用 Docker 部署:镜像怎么选、状态存在哪、升级卡住怎么救
用 Docker 跑 OpenClaw 网关,大部分人卡住的地方不是”怎么把容器起来”,而是起来之后的三件事:镜像里到底带了什么、哪些目录不挂载就会丢、以及换了新镜像之后容器为什么开始反复重启。这三件事官方文档都写了,只是分散在 /install/docker、/install/docker-vm-runtime 和 /install/clawdock 三篇里。
还有一个前置判断得先做:OpenClaw 的官方口径是 Docker 是可选的。它适合两种场景——想要一个隔离的、随时可以扔掉的网关环境,或者宿主机上不想装本地依赖。如果你本来就在自己的机器上做开发,官方建议直接走普通安装流程,别绕 Docker 这一圈。
另外要澄清一个容易混淆的概念:「网关跑在容器里」和「Agent 沙箱用 Docker」是两回事。沙箱默认是关闭的,而且并不要求网关本身跑在容器里——沙箱是让网关留在宿主机、只把 Agent 的工具执行(shell、文件读写等)关进隔离容器。默认沙箱后端只用 docker CLI,把 backend 设成 "podman" 可以直接走原生 Podman,此外还有 SSH 和 OpenShell 两种后端。想搞清楚这条边界,可以配合看沙箱、工具策略与提权的分界。
开始之前:把内存这条硬门槛核清楚
官方列的前置条件里,最容易翻车的是内存。文档写明镜像构建至少需要 2 GB RAM,在 1 GB 的主机上 pnpm install 可能被 OOM 杀掉,表现为退出码 137。这个数字值得记,它在文档里出现了三次:Docker 安装页的前置条件、故障排查折叠块(“OOM-killed during image build (exit 137)”),以及 VM runtime 页——构建时报 Killed 或 137,就是 VM 内存不够,换更大的机器规格再试。
其余前置条件是 Docker Desktop(或 Docker Engine)加 Docker Compose v2、足够放镜像和日志的磁盘。部署在 VPS 或公网主机上时,官方特别点名要先看网络暴露的安全加固,尤其是 Docker 的 DOCKER-USER 防火墙链。
如果构建过程中报 ResourceExhausted、cannot allocate memory,或者在 tsdown 阶段中止,官方给的办法是调大 Docker builder 的内存上限,或者用更小的显式堆重试:
OPENCLAW_DOCKER_BUILD_NODE_OPTIONS=--max-old-space-size=4096 OPENCLAW_DOCKER_BUILD_TSDOWN_MAX_OLD_SPACE_MB=4096
自己构建还是拉官方镜像
在仓库根目录跑 ./scripts/docker/setup.sh 会在本地构建出 openclaw:local。想用预构建镜像就先设环境变量:
export OPENCLAW_IMAGE="ghcr.io/openclaw/openclaw:latest"
./scripts/docker/setup.sh
预构建镜像首先发布到 GitHub Container Registry,GHCR 是发布自动化、锁定部署和溯源校验的主 registry;同一次发布也会推一份 Docker Hub 镜像 openclaw/openclaw。官方明确要求只用 ghcr.io/openclaw/openclaw 或 openclaw/openclaw 这两个来源,避开非官方镜像站——那些站点不共享 OpenClaw 的发布节奏和保留策略。
标签规则值得单独看一眼,因为它决定了你的部署会不会”自己动”:
| 标签形态 | 含义 |
|---|---|
2026.2.26 | 具体版本标签,不可变 |
2026.2.26-beta.1 | 预发布版本 |
latest / main | 稳定版发布时移动 |
extended-stable | 只有 trailing-month 的 Gateway 发布会移动它 |
slim / -browser 等变体 | 另有 main-slim、extended-stable-slim、latest-browser、main-browser、extended-stable-browser |
2026.8.1-r20260820 | 每周刷新产生的带日期标签,不可变 |
默认镜像捆绑了 codex 和 diagnostics-otel 两个插件;-browser 变体额外烤入了 Chromium,用沙箱浏览器工具时不用等首次运行时装 Playwright。移动标签每周会从同一份发布源码重建一次,好在两个 OpenClaw 版本之间也能吃到系统安全更新——反过来说,不希望部署跟着标签走,就得钉死版本标签或 -rYYYYMMDD 日期标签。
离线主机先传镜像再加载:
docker load -i openclaw-image.tar
export OPENCLAW_IMAGE="ghcr.io/openclaw/openclaw:latest"
./scripts/docker/setup.sh --offline
--offline 会先确认 OPENCLAW_IMAGE 在本地确实存在,关掉 Compose 的隐式拉取和构建,然后走正常流程:.env 同步、权限修复、onboarding、网关配置同步、Compose 启动。如果同时开了 OPENCLAW_SANDBOX=1,离线 setup 还会检查沙箱镜像在不在、是不是过期的;缺了就直接退出,而不是改坏沙箱配置再报一个假的成功。
onboarding 与无人值守启动
setup 脚本会自动跑 onboarding:问 provider API key、生成网关 token 写进 .env、建 auth-profile 密钥目录、用 Compose 把网关拉起来。这一步有个实现细节解释了很多人的疑惑——启动前的 onboarding 和配置写入都是直接通过 openclaw-gateway 服务执行的(带 --no-deps --entrypoint node),因为 openclaw-cli 共享网关的网络命名空间,只有网关容器存在之后才能用。
装完打开 http://127.0.0.1:18789/,把 .env 里的 token 粘进设置页;忘了地址就跑 docker compose run --rm openclaw-cli dashboard --no-open。
如果是无人值守的容器主机,官方给的路子是把 provider、网关、渠道三类凭据全放进 Compose 的 .env,让一次性 bootstrap 容器和长期运行的网关拿到同一份值,然后用 -T 关掉伪终端跑非交互 onboarding:
docker compose run -T --rm --no-deps --entrypoint node openclaw-gateway \
dist/index.js onboard --non-interactive --accept-risk --skip-health \
--mode local \
--auth-choice openai-api-key \
--secret-input-mode ref \
--gateway-auth token \
--gateway-token-ref-env OPENCLAW_GATEWAY_TOKEN \
--skip-channels \
--no-install-daemon
渠道那条命令用 --use-env 时,凭据查找留在环境变量里,不会把 token 复制进 openclaw.json——所以 bootstrap 之后 .env 里的 TELEGRAM_BOT_TOKEN 不能删,运行中的网关还要读同一个变量。另外,如果插件声明的环境变量缺失,渠道命令会在改配置之前就失败,不会留下半截配置。启动后再改渠道配置,网关的配置监视器会自动热重载受影响的渠道。
还有一个坑藏在手动构建流程里:Docker 构建上下文排除了 .git,源码身份得靠构建参数传进去,镜像的 About 页面才能报出正确的 commit 和构建时间戳。setup.sh 会自动解析并传这两个值,手动 docker build 就得自己加 --build-arg GIT_COMMIT=... 和 --build-arg OPENCLAW_BUILD_TIMESTAMP=...。
网络:bind 模式、host.docker.internal 与 Bonjour
scripts/docker/setup.sh 默认把 OPENCLAW_GATEWAY_BIND 设成 lan,这样配合 Docker 端口发布,宿主机浏览器访问 http://127.0.0.1:18789 才通;换成 loopback 则只有容器网络命名空间内的进程能直接访问网关。官方特别提醒:gateway.bind 只能填模式值(lan / loopback / custom / tailnet / auto),不要写 0.0.0.0、127.0.0.1 这类主机别名。
第二个高频问题是宿主机上的本地模型服务连不上。容器里的 127.0.0.1 指的是容器自己,不是宿主机,正确写法是 host.docker.internal:
| Provider | 宿主机默认地址 | Docker 环境下的地址 |
|---|---|---|
| LM Studio | http://127.0.0.1:1234 | http://host.docker.internal:1234 |
| Ollama | http://127.0.0.1:11434 | http://host.docker.internal:11434 |
同时宿主机上的服务本身也得监听 Docker 能到达的地址,比如 lms server start --port 1234 --bind 0.0.0.0 或 OLLAMA_HOST=0.0.0.0:11434 ollama serve。自带的 docker-compose.yml 在 Linux Docker Engine 上会把 host.docker.internal 映射到宿主网关(Docker Desktop 在 macOS/Windows 上原生提供同名别名);如果你用自己的 Compose 文件或 docker run,要自己加 --add-host=host.docker.internal:host-gateway。
第三个是 Bonjour/mDNS。Docker 桥接网络通常不能可靠转发 224.0.0.251:5353 的组播,所以在 OPENCLAW_DISABLE_BONJOUR 未设置时,自带的 Bonjour 插件检测到自己在容器里就会自动关掉局域网广播,免得反复重试组播把自己搞成崩溃循环。想强制关就设 1,想强制开设 0——但官方限定只在主机网络、macvlan 或其它确认 mDNS 组播可用的网络上才这么做。Docker 主机上更靠谱的做法是用已发布的网关 URL、Tailscale 或广域 DNS-SD。
健康探针:三个端点各管一段
镜像内置的 HEALTHCHECK ping 的是 /healthz,连续失败会把容器标成 unhealthy,编排系统据此重启或替换。三个探针端点都不需要鉴权:
curl -fsS http://127.0.0.1:18789/healthz # 存活
curl -fsS http://127.0.0.1:18789/startupz # 启动与流量准入
curl -fsS http://127.0.0.1:18789/readyz # 深度、渠道感知的就绪
官方的建议很具体:编排系统的 startup / readiness 探针用 /startupz,这样某个渠道账号挂了不会把本来健康的网关和 Control UI 一起摘出服务;而 /readyz 留给监控用——它会有意把渠道的硬失败算作未就绪。想要一份带鉴权的深度健康快照,用 docker compose exec openclaw-gateway sh -lc 'node dist/index.js gateway health --token "$OPENCLAW_GATEWAY_TOKEN"'。两套健康检查的口径差异可以再看health 与 heartbeat 的区别。
什么必须持久化,什么可以随手删
VM runtime 文档里有一句话是这套部署的心法:OpenClaw 跑在 Docker 里,但 Docker 不是事实来源。所有长期状态都得能扛住重启、重建和重启机器。官方给了一张归属表:
| 组件 | 位置 | 持久化机制 |
|---|---|---|
网关配置(含 openclaw.json) | /home/node/.openclaw/ | 宿主卷挂载 |
| 渠道/供应商凭据 | /home/node/.openclaw/credentials/ | 宿主卷挂载 |
| 模型认证档案 | /home/node/.openclaw/agents/ | 宿主卷挂载 |
| 遗留 OAuth 密钥文件 | /home/node/.config/openclaw/ | 宿主卷挂载(只读兼容) |
| Skill 配置 / Agent 工作区 | /home/node/.openclaw/skills/、workspace/ | 宿主卷挂载 |
| 插件包 | /home/node/.openclaw/npm、/git | 宿主卷挂载 |
| 外部二进制 | /usr/local/bin/ | Docker 镜像,必须构建时烤入 |
| Node 运行时 / 系统包 | 容器文件系统 | Docker 镜像,禁止运行时安装 |
| 容器本身 | 临时 | 可随时销毁 |
Compose 默认把 OPENCLAW_CONFIG_DIR 绑到 /home/node/.openclaw、OPENCLAW_WORKSPACE_DIR 绑到工作区、OPENCLAW_AUTH_PROFILE_SECRET_DIR 绑到 /home/node/.config/openclaw。变量没设时回落到 ${HOME} 下,连 HOME 都没有就落到 /tmp,保证 docker compose up 不会吐出空 source 的卷声明。auth-profile 密钥目录存的是 OAuth 令牌材料的本地加密密钥,官方要求跟主机状态放在一起,但要和 OPENCLAW_CONFIG_DIR 分开。
“外部二进制必须烤进镜像”这条要展开说。VM runtime 页把运行时装二进制直接称为陷阱:运行时装的东西一重启就没了。Debian 包用现成的构建参数 OPENCLAW_IMAGE_APT_PACKAGES;下载型的发布二进制则要把下载和安装命令加进仓库根 Dockerfile 的最终运行阶段,位置在包安装块之后、USER node 之前,同时保留原有的非 root uid 1000、tini 入口、内置健康检查和 openclaw 符号链接。仓库 Dockerfile 对 Node 和 Bun 基础镜像做了 digest 锁定,官方要求保留这些 pin,不要换成浮动的 FROM node:24-bookworm。ARM 架构的 VM 要选 arm64 资产。
磁盘增长的热点官方也列了:media/、每个 Agent 的 SQLite 数据库、遗留的会话 JSONL 转写、共享 SQLite 状态库、已安装插件的包根目录,以及 /tmp/openclaw/ 下的滚动日志文件。
换镜像后容器反复重启:先别删卷
这是 Docker 部署最值得提前知道的一段。换新镜像但保留同一份挂载的状态/配置时,新网关会在报告就绪前先跑启动期安全的升级迁移和插件收敛——常规镜像升级不需要额外跑 openclaw doctor --fix。
但如果启动阶段没法安全完成这些修复,网关会选择退出,而不是谎报健康。加上重启策略,你在 Docker、Podman 或 Kubernetes 上看到的现象就是网关容器一直重启。此时保留挂载的状态卷,用同一个镜像跑一次 doctor:
docker run --rm -v <openclaw-state>:/home/node/.openclaw <image> openclaw doctor --fix
podman run --rm -v <openclaw-state>:/home/node/.openclaw <image> openclaw doctor --fix
Kubernetes 上就用一次性 Job 或 debug pod 挂同一个 PVC 跑同样的命令,然后重启 Deployment 或 StatefulSet。容器重新跑起来之后,再跑一次只读的部署预检 docker compose run --rm openclaw-cli doctor --json。doctor 输出怎么逐条对号入座,见 doctor 体检报告怎么读。
VM 上的日常更新流程简单得多:git pull、docker compose build、docker compose up -d。
权限、DNS 与镜像里没有的东西
几个容易被误判成”软件有 bug”的现象,官方都给了归因:
/home/node/.openclaw上的 EACCES:镜像以node(uid 1000)身份运行,宿主机的 bind mount 得归 uid 1000 所有,用sudo chown -R 1000:1000修。同一个不匹配还可能表现成blocked plugin candidate: suspicious ownership后跟plugin present but blocked——进程 uid 和挂载的插件目录属主对不上。官方倾向保持默认 uid 1000 并修挂载属主,而不是改成 root。openclaw plugins install报EAI_AGAIN:部分 Docker Desktop 环境在NET_RAW被 drop 之后,共享网络的openclaw-cli边车 DNS 解析会失败。官方给的是一次性 override(cap_drop: !reset [])只用于那条需要访问 registry 的命令,不建议当默认调用方式;而且如果你已经创建了长期运行的openclaw-cli容器,必须用同样的 override 重建它——docker compose exec改不了已创建容器的 Linux capability。- 镜像里没有 Homebrew:官方镜像不带 brew,onboarding 时会在没有 brew 的 Linux 容器里隐藏那些只支持 brew 的 skill 依赖安装器。需要的话用
OPENCLAW_IMAGE_APT_PACKAGES装 Debian 包、OPENCLAW_IMAGE_PIP_PACKAGES装 Python 依赖(构建时执行python3 -m pip install --break-system-packages,所以要钉版本、只用可信索引)。 - 官方镜像不预装 Claude Code:要在容器里装并登录,再持久化容器家目录,否则镜像升级会连二进制带登录态一起抹掉。这里有个反直觉的点:setup 脚本总是用当前 shell 的值重写
.env,它自己不读这个文件——已有安装要先set -a; . ./.env; set +a把现有值加载回来再跑。
还有一条共享信任边界要心里有数:openclaw-cli 用 network_mode: "service:openclaw-gateway",好让 CLI 命令通过 127.0.0.1 访问网关。Compose 在两个服务上都 drop 了 NET_RAW/NET_ADMIN 并开了 no-new-privileges,但共享网络本身就是信任边界,别当成隔离。开沙箱时官方还划了一条硬线:绝不要把宿主的 Docker socket 挂进 Agent 沙箱容器;setup 脚本也只在沙箱前置条件通过后才挂 docker.sock,没通过就把 agents.defaults.sandbox.mode 重置为 off。
ClawDock:把 compose 命令缩短
经常用 Docker 跑 OpenClaw 的话,官方有个叫 ClawDock 的 shell 辅助层,把 docker compose ... 长命令换成 clawdock-start、clawdock-dashboard、clawdock-fix-token 这类短命令。安装是拉一个脚本到 ~/.clawdock/ 并 source 进 shell 配置;helpers 首次使用会自动探测 OpenClaw checkout(查 ~/openclaw、~/projects/openclaw 这类路径)并缓存到 ~/.clawdock/config,checkout 在别处就设 CLAWDOCK_DIR。旧的 scripts/shell-helpers/ 路径已移除,从老路径装过的要重装。
命令覆盖启停(start/stop/restart/status/logs)、容器访问(shell/cli/exec)、Web UI 与配对(dashboard/devices/approve)、维护(fix-token/update/rebuild/clean)和一批工具类命令(health/token/cd/config/show-config/workspace/help)。首次流程是 clawdock-start → clawdock-fix-token → clawdock-dashboard,浏览器提示需要配对就 clawdock-devices 加 clawdock-approve <request-id>。
值得单独记的是它对两个 .env 的区分(这个分工在 Docker 安装文档里也一样):docker-compose.yml 旁边的项目 .env 放 Docker 相关的值(镜像名、端口、OPENCLAW_GATEWAY_TOKEN),clawdock-token 读的就是这一份;~/.openclaw/.env 是挂进容器的、OpenClaw 自己管理的环境变量型密钥。clawdock-fix-token 做的事是把项目 .env 里的 token 复制进容器配置的 gateway.remote.token 和 gateway.auth.token 并重启网关。
什么时候别用 Docker,以及文档没覆盖的部分
先说不适用的场景。官方自己就说了:如果你在本机做开发,走普通安装流程更直接,容器只会多一层网络和文件权限的心智负担。上面那些 host.docker.internal、uid 1000、Bonjour 组播的问题,本地安装一个都不会遇到。四种安装方式各自适合谁,可以看安装方式怎么选。
第二类是只有 1 GB 内存的小机器。2 GB 是构建镜像的门槛,不是运行门槛,但要在这台机器上构建,退出码 137 基本注定;退路是用预构建镜像或在别处构建好再 docker load。
还有几件文档没给答案的事,得说清楚免得踩空:官方文档没有给出容器的运行时内存/CPU 建议值,也没有任何吞吐或延迟数字,别按”多少内存能带多少并发”去规划。Crabhelm 管理的网关不消费这套 OCI 镜像,它走另一条独立交付路径。至于安全扫描结果,官方提醒扫描器总数里会包含 Debian 标为 wont-fix 的发行版问题,看到一堆红字先分清是不是这类。
最后一个提醒:启用了 OPENCLAW_EXTRA_MOUNTS 或 OPENCLAW_HOME_VOLUME 时,setup 脚本会生成 docker-compose.extra.yml,它必须排在你自己维护的 docker-compose.override.yml 之后,即 -f docker-compose.yml -f docker-compose.override.yml -f docker-compose.extra.yml。顺序错了的表现往往是”配置明明写了却没生效”,排查起来相当费时间。
延伸阅读
- 从头读起:OpenClaw 架构总览:网关、Agent、渠道、节点、插件五层各管什么,出问题该往哪查
- 本专题共 40 篇,完整分组目录见专题页
- 树莓派部署 OpenClaw:型号怎么选、swap 怎么加、SD 卡为什么是最大隐患
- 在 Windows 上跑 OpenClaw:Hub、原生 CLI 与 WSL2 三条路怎么选、怎么避坑
本文依据 OpenClaw 官方仓库(github.com/openclaw/openclaw)docs/ 下的官方文档整理,核对日 2026-08-17。
我们没有安装或运行过 OpenClaw,因此不涉及界面外观、操作手感与实测耗时的任何描述;
文中的默认值、命令与配置项均为文档口径,不构成对实际运行结果的保证。
该项目迭代很快,请以仓库最新内容为准。接入即时通讯平台前,请自行确认所在平台的规则与合规要求。