1. 为什么选 Docker 来跑 OpenClawOpenClaw 这类偏个人化的智能体工程最麻烦的不是核心逻辑而是环境。你本地装了 Python 3.11朋友那边是 3.9再换到服务器就是 Conda 里的 3.10依赖从requirements.txt到系统库再到编译产物任何一个环节错位整个服务就起不来。我在最开始搞这套东西的时候就因为 protobuf 版本冲突在本地环境里折腾了差不多一个通宵最后把系统自带的 Python 环境搞到半残。换到 Docker 之后这类问题基本绝迹。镜像里自带固定的基础系统、固定版本的运行时、固定的依赖树宿主机上是什么发行版、装了什么乱七八糟的开发包完全不影响容器内部。你只需要保证 Docker 本身能跑剩下的就是拉镜像、起容器、映射端口、挂卷。OpenClaw 本身的设计也对容器化挺友好。它包含了一个可独立运行的 Web 服务负责交互和技能编排、一个后端推理调度模块负责调用本地或者远程的模型接口两者之间通过本机端口通信。这种前后端分离、接口清晰的项目结构天然就适合用 Docker Compose 把它们组合起来。相比在物理机上裸跑用 Docker 部署的另外一个实际好处是升级回滚极其方便新版本有问题直接换回旧镜像十秒钟的事不用再担心把宿主机上的依赖搞得一团糟。这篇文章我会把从零开始的完整路径走一遍编译源码构建镜像、迁移配置和数据跨机器搬移、Token 配置模型接口凭证的正确传递。实践环境是 Ubuntu 22.04 加 Docker Desktop 4.xOpenClaw 的版本基于主分支最新代码。内容偏向可以直接照做的实战步骤每段都会穿插我实际踩过的坑。2. 部署前的准备Docker 环境与镜像基础2.1 安装 Docker 的几个关键判断如果你是在 Windows 或 macOS 上做开发调试Docker Desktop 依然是首选它自带的 GUI 对新手友好Compose、卷管理、日志面板全都有。我自己的主力机是 Linux所以这里以 Ubuntu 为例说明。Ubuntu 上安装 Docker 引擎官方推荐用 apt 仓库方式装sudo apt-get update sudo apt-get install ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc sudo chmod ar /etc/apt/keyrings/docker.asc echo \ deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release echo $VERSION_CODENAME) stable | \ sudo tee /etc/apt/sources.list.d/docker.list /dev/null sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin这里有个细节旧版本文档里会让你装docker-compose独立的 Python 包现在官方推荐的是docker-compose-plugin提供的是docker compose子命令。两者不完全等价新项目一律用插件版。装完以后验证一下sudo systemctl enable --now docker sudo docker run --rm hello-world如果hello-world能正常打印那一段信息Docker 引擎就绪。这里最常见的坑是当前用户不在docker用户组里导致每次都要sudo docker。这很烦但更危险的是直接把普通用户加进docker组会带来权限提升风险——等价于给了这个用户 root 级别的容器管理权。你自己开发机无所谓但生产服务器上要谨慎。我自己的做法是开发机加组生产环境坚持用服务账号配合密钥登录。2.2 基础镜像选型不是越新越好OpenClaw 源码构建时依赖一堆编译工具链。它本身的主程序是 Python 写的但部分本地扩展模块需要 C 编译器来构建另外技能编排器里有个组件需要独立的二进制运行时。我最终的镜像基于 Python 3.11 slim 版本定制FROM python:3.11-slim-bookworm为什么用 slim 而不是完整版完整版镜像动辄 1GB 以上绝大多数都是你用不到的文档和系统包拉取慢、占磁盘、攻击面还大。slim 版精简掉了这些但保留了构建和运行 Python 应用必需的包管理器。为什么用 bookworm 而不是 bullseye 或者 jammy因为 bookworm 是当前 Debian 的稳定分支软件版本新编译依赖好装。等未来 Debian 有新版 stable再迁移过去。2.3 一个必须提前处理的依赖编译工具链OpenClaw 的一个核心依赖在编译期需要 C 扩展。这个扩展是它的技能匹配引擎的一部分负责把所有已安装技能的描述向量化。源码安装时pip install会自动尝试构建这个扩展如果基础镜像里没有 gcc 和 Python 开发头文件构建会直接报错错误信息大概是error: command gcc failed。还会遇到一个经典报错fatal error: Python.h: No such file or directory。这是缺少 Python 头文件导致的属于没装python3-dev也就是 Debian 系统里的python3-dev包。所以 Dockerfile 里必须有一段RUN apt-get update apt-get install -y --no-install-recommends \ build-essential \ python3-dev \ libffi-dev \ libssl-dev \ git \ curl \ rm -rf /var/lib/apt/lists/*--no-install-recommends这个参数一定要加否则 apt 会把一堆推荐包装进来镜像体积膨胀。装完依赖立刻清理 apt 缓存因为它们在同一个 RUN 层里后续构建不会再次产生迷雾。3. 编译 OpenClaw源码构建镜像的全过程3.1 拉源码和锁版本很多人忽略版本锁定。直接从主分支git clone有个问题你今天拉到的代码和下周拉到的可能行为完全不同之前能跑通的配置可能因为接口变化就失效了。我自己经历过一次某天升级后OpenClaw 的配置系统从 JSON 换成 YAML我的启动脚本直接全部作废。所以正确的做法是固定 commitgit clone https://github.com/your-openclaw-mirror/openclaw.git /opt/openclaw-src cd /opt/openclaw-src git checkout v0.9.2v0.9.2是我目前使用的版本号。如果你没有指定版本至少git checkout到某个稳定的 tag这个 tag 对应的镜像构建结果才具备可复现性。3.2 构建脚本的坑别一股脑装全部依赖OpenClaw 的requirements.txt里分成几个部分核心依赖、技能开发相关、Web UI 相关、测试相关。如果直接执行pip install -r requirements.txt会把测试框架、代码格式化工具等一堆运行时不必要的东西装进镜像。我的做法是在 Dockerfile 里只安装核心加 Web 那两组COPY /opt/openclaw-src/requirements-core.txt /tmp/requirements-core.txt COPY /opt/openclaw-src/requirements-web.txt /tmp/requirements-web.txt RUN pip install --no-cache-dir -r /tmp/requirements-core.txt -r /tmp/requirements-web.txt--no-cache-dir是为了避免 pip 在镜像里缓存下载的 wheel 包这些缓存对运行毫无意义只会让镜像变大。3.3 编译环境的清理多阶段构建的价值第一次我图省事直接在运行镜像里做编译结果镜像体积膨胀到 2.8GB。后来改成多阶段构建才把体积压下来。思路很简单第一阶段builder里安装全部编译工具链把 C 扩展编译成.so文件第二阶段runtime只拷贝编译产物和 Python 代码运行时镜像里不再需要 gcc。# builder 阶段 FROM python:3.11-slim-bookworm AS builder WORKDIR /build RUN apt-get update apt-get install -y --no-install-recommends \ build-essential python3-dev libffi-dev libssl-dev git curl \ rm -rf /var/lib/apt/lists/* COPY requirements-core.txt requirements-web.txt ./ RUN pip wheel --no-cache-dir --wheel-dir /wheels \ -r requirements-core.txt -r requirements-web.txt # runtime 阶段 FROM python:3.11-slim-bookworm AS runtime WORKDIR /app COPY --frombuilder /wheels /wheels COPY --frombuilder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages COPY openclaw/ ./openclaw/ COPY scripts/ ./scripts/ RUN pip install --no-cache-dir --no-index --find-links/wheels openclaw注意第二阶段没有build-essential运行期的 Python 进程不需要编译器这样既省空间也减小了攻击面。最终镜像体积能从 2.8GB 降到 980MB 左右。3.4 编译过程中三个常见报错实录第一个常见报错gcc: error: unrecognized command line option ‘-stdc17’。这个通常是因为基础镜像里的 gcc 版本太老。Debian bullseye 自带的 gcc 10 对 C17 支持是没问题的但如果你用了更老的 Ubuntu 20.04 基础镜像gcc 9 虽然支持部分扩展库编译时还会因为头文件路径问题踩坑。解决办法就是升级到新一点的基础镜像。第二个清库存报错ERROR: Failed building wheel for msgpack。这个依赖是消息队列序列化组件。如果是纯 Python 环境装不了的时候可以降级到纯 Python 实现但在 OpenClaw 里msgpack 的 C 加速版本对性能影响明显尤其技能调度场景下大量消息要序列化所以我还是推荐装好编译链直接编译。第三个报错相对隐蔽发生在构建快结束的时候ModuleNotFoundError: No module named setuptools_rust。这是有些依赖想用 Rust 写扩展需要setuptools-rust这个辅助包。解决办法很简单在构建阶段提前装RUN pip install setuptools-rust这个坑在 Windows 上更普遍Linux 因为系统自带编译器往往能自动 fallback不过保险起见我都是直接在构建阶段加上。3.5 要不要自己编译直接拉镜像方案对比如果你不想折腾编译OpenClaw 官方也发布编译好的镜像。我用过一次流程极其顺滑docker pull openclaw/openclaw:latest docker run -d -p 8080:8080 openclaw/openclaw:latest但那是在一个没有外网特殊需求的干净服务器上。自己编译的好处是你可以随时改源代码、加自定义依赖、甚至给 C 扩展打补丁。坏处是第一次构建很费时间费时在 pip 下载依赖上。我实测构建一次大约需要 15 到 25 分钟取决于网络速度和机器性能。我的建议是如果你只是要跑起来试试功能直接拉官方镜像如果你要二次开发或者部署到隔离环境自己编译。两种路线本文都覆盖。4. 数据与配置迁移从本地环境搬到容器4.1 迁移的三个核心对象OpenClaw 部署完成后真正需要迁移的是三个东西配置文件、技能包数据和会话历史数据库。很多人只迁移配置结果发现技能全没了历史对话也丢了最后还得重新折腾一遍。三个对象分别对应容器里的这些位置对象默认路径说明配置文件/app/config/json 或 yaml 格式含模型参数、端口、Token 引用技能包数据/app/skills/已经安装的技能描述和依赖元数据会话历史/app/data/SQLite 数据库文件存聊天记录、任务状态4.2 迁移用的目录挂载方案在 Docker 里最稳妥的做法是这三个目录全部挂到宿主机这样才能保证容器重建后数据不丢。services: openclaw: image: openclaw/openclaw:0.9.2 volumes: - ./config:/app/config - ./skills:/app/skills - ./data:/app/data这一套做完迁移就变成了“拷贝目录”。从旧机器把三个目录打包到新机器解包再启动容器一切照旧。4.3 迁移过程中最容易出错的环节我发现迁移过程中最容易出错的不是文件本身而是文件权限。Docker 容器里的进程通常以普通用户运行而宿主机拷贝过来的文件可能属于 root 用户或者权限是 644缺少写权限容器里的进程一写数据库就报Permission denied。我遇到过最典型的一次容器能启动Web 界面也能打开但技能调度一执行就报sqlite3.OperationalError: unable to open database file。查了半天最后发现是data/目录的属主不对里面数据库文件属于宿主机 root容器内进程无权写。解决办法是在宿主机上统一设置归属chmod -R 777 ./data chmod -R 755 ./config ./skills777只在单机开发环境用生产环境建议改用chown指定到容器内用户的 UID。4.4 版本差异导致的配置迁移陷阱还有一个隐蔽问题旧版本的配置文件格式可能和新版本不兼容。我在从 0.8.x 升到 0.9.x 时配置文件里有个字段从model_platform改成了llm_provider结果容器起来后 Web 界面始终报“模型未配置”。排查过程很痛苦日志里只有一行Error: provider not found完全没有指明是配置字段过期。最后是对比了新旧两版的配置样例文件才找出字段名差异。所以建议迁移前先把官方样例配置拿到逐个字段核对尤其注意大版本升级。不要盲目拷贝旧配置。5. Token 配置让 OpenClaw 真正能调用模型5.1 Token 在 OpenClaw 里的角色OpenClaw 本身不提供底层大模型能力它通过调用外部模型 API 来执行推理。这些 API 的访问凭证就是 Token。Token 配置错了整个系统启动可能正常但一触发任何需要模型的技能就会立刻报 401。Token 在 OpenClaw 配置里的位置在 config 主文件的llm段llm: provider: openai model: gpt-4o-mini api_key_env_var: OPENCLAW_API_KEY temperature: 0.2注意我用的是api_key_env_var它指定从环境变量读取 Token而不是直接把 Token 硬编码在配置文件里。这样做有两个好处第一配置文件可以放版本库而不用担心泄露 Token第二换 Token 时只需要改容器环境变量不需要改配置文件。5.2 环境变量传递的三种写法用 Docker Compose 时环境变量有三种传递方式我按推荐程度排序方式一直接在 compose 文件的 environment 字段里引用宿主机环境变量services: openclaw: image: openclaw/openclaw:0.9.2 environment: - OPENCLAW_API_KEY${OPENCLAW_API_KEY}这个$HOME/.env文件里定义OPENCLAW_API_KEYsk-xxxxxx这是最干净的做法.env文件不要提交到 git加入.gitignore。方式二直接用 compose 的env_fileservices: openclaw: image: openclaw/openclaw:0.9.2 env_file: - ./openclaw.env方式三把 Token 写进配置文件的某个字段。这个方式最直接但我极其不推荐原因是它会让 Token 出现在镜像层里。一旦镜像被 push 到仓库Token 就永久泄露了。5.3 Token 的类型和获取OpenClaw 支持大多数主流模型提供商。常见三类 TokenOpenAI 风格以sk-开头在平台后台生成。Anthropic 风格以sk-ant-开头同样在后台创建。本地 Ollama 或 vLLM 服务通常不需要 Token留空或者填一个占位符。如果你是连本地 Ollama 服务compose 里的配置就简单很多llm: provider: ollama model: qwen2.5:7b base_url: http://host.docker.internal:11434 api_key_env_var: DUMMY因为容器访问宿主机上的 Ollama 需要指定host.docker.internal这个特殊域名。在 Linux 上默认没有这个域名需要在extra_hosts里手动加extra_hosts: - host.docker.internal:host-gateway这个坑我踩过一次。容器内访问 Ollama 一直超时后来加上host-gateway立即通了。5.4 Token 配置验证的正确姿势配置完之后别急着打开 Web 界面做完整交互先用命令行做一次最小验证docker exec -it openclaw-cli python scripts/smoke_test.py这个脚本会调用一次配置好的模型接口如果 Token 有问题这里会直接报AuthenticationError或401。如果一切正常会打印出模型返回的一段测试文本然后你再去 Web 界面做深度测试。我清理过很多次这种场景Web 界面看起来一切正常输入消息时却直接报接口错误日志里才能看到401 invalid api key如果一开始就做冒烟测试能省半小时排查时间。5.5 查看运行日志定位 Token 问题容器启动后所有日志可以通过docker logs openclaw-serverToken 相关报错的关键字一般是401 UnauthorizedMissing bearer tokenAuthentication failedInvalid API key如果日志里出现这些不要怀疑先看 Token 有没有正确传进容器docker exec openclaw-server env | grep OPENCLAW检查环境变量存在与否这一步比看任何日志都直接。6. 用 Docker Compose 编排 OpenClaw 全家桶6.1 一个完整的 Compose 文件长什么样如果只跑 OpenClaw 本体一个docker run就够。但实际使用中通常还需要 SQLite 的备份任务、日志轮转、健康检查。我搭建时用的 compose 文件长这样version: 3.8 services: openclaw: image: openclaw/openclaw:0.9.2 container_name: openclaw-server ports: - 8080:8080 volumes: - ./config:/app/config - ./skills:/app/skills - ./data:/app/data environment: - OPENCLAW_API_KEY${OPENCLAW_API_KEY} env_file: - ./openclaw.env extra_hosts: - host.docker.internal:host-gateway restart: unless-stopped healthcheck: test: [CMD, curl, -f, http://localhost:8080/health] interval: 30s timeout: 5s retries: 3 start_period: 10s6.2 健康检查的作用健康检查不是锦上添花。OpenClaw 启动时如果外部模型接口不可用主进程可能会挂起但不退出容器状态显示 running实际已经无法处理请求。有了 healthcheck 之后容器会在一段时间后标记为 unhealthy配合restart: unless-stopped系统才能自动重启它。有两次我都靠 healthcheck 发现容器假死一次是模型接口密钥过期一次是 SQLite 数据库表锁死。如果没有健康检查这两次会害我手动重启无数次。6.3 数据备份的策略容器化部署之后备份变得异常简单。因为所有持久化数据都在./data目录里直接用宿主机定时任务打包就行# 每天晚上 2 点备份 0 2 * * * tar -czf /backup/openclaw-$(date \%Y\%m\%d).tar.gz -C /opt/openclaw/data .我保留最近 7 天的备份删掉更旧的0 3 * * * find /backup -name openclaw-*.tar.gz -mtime 7 -exec rm {} \;注意备份前最好先把容器停下来否则 SQLite 文件可能不一致。如果不想停机可以用 SQLite 在线备份工具在容器里执行docker exec openclaw-server sqlite3 /app/data/claw.db .backup /tmp/backup.db再把/tmp/backup.db拷贝出来。这个方式可以不打断服务而且一致性好。6.4 升级镜像的正确顺序每次升级 OpenClaw 镜像我的操作步骤是这样先备份 data 目录。拉新镜像docker compose pull。停止并移除旧容器docker compose down。启动新容器docker compose up -d。检查日志docker compose logs -f --tail200。触发器一次健康检查。这套流程看起来无聊但能避免 90% 的升级事故。我见过太多人直接在 running 容器里改配置、重启服务结果配置语法错误整个服务直接挂掉。7. 常见问题与排查技巧实录7.1 排查问题前你必须做的一件事任何问题发生第一步永远是看日志而不是凭感觉改配置。下面这段命令能帮你把最近 200 行日志打印到屏幕同时实时跟踪新日志docker logs -f --tail200 openclaw-server日志里没有明确报错时进入容器检查运行状态docker exec -it openclaw-server bash ps aux curl localhost:8080/health这能快速判断是进程假死还是外部接口问题。7.2 常见问题速查表现象可能原因排查命令与解法容器反复重启配置语法错误进程启动即退出docker logs openclaw-server修正配置后重启Web 界面能开但消息全报错Token 未传入容器 / 模型接口不可达docker exec openclaw-server env技能安装后不生效volumes 未正确挂载 / 技能目录权限不足docker exec openclaw-server ls /app/skills调整目录权限数据库锁死database is lockedSQLite 并发写冲突或宿主机磁盘 I/O 瓶颈重启容器减少并发任务将 data 目录迁移到 SSD端口映射失败address already in use8080 已被其他程序占用ss -tlnp冷启动后首次请求很慢模型接口冷加载 / 容器 DNS 解析慢配置healthcheck.start_period加长或在 compose 中配置dns: 8.8.8.87.3 一个容易被忽略的坑宿主机防火墙这类问题最容易出现在服务器部署上。容器起来了本地回环访问一切正常但局域网其他机器访问 8080 端口就是不通。排查方向不要一开始就盯容器先看防火墙sudo ufw status # 或 sudo iptables -L -n | grep 8080如果是云服务器还要检查安全组规则确保 8080 端口对外放行。7.4 容器内时间不同步这个问题很隐蔽但一旦踩中会让你摸不着头脑容器内时间是 UTC数据库时间戳和日志时间戳全部偏移 8 小时。如果业务对时间敏感一定要在 compose 里挂载宿主机的时区文件volumes: - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro或者干脆通过环境变量environment: - TZAsia/Shanghai7.5 网络层面的隔靴搔痒我最后提一下容器网络模型。默认桥接模式下容器可以访问外网但宿主机访问容器内服务需要端口映射容器访问宿主机服务需要用host.docker.internal。如果你把 OpenClaw 和 Ollama 放在同一个 compose 网络里可以使用服务名直接通信services: openclaw: ... ollama: image: ollama/ollama:latest ... llm: provider: ollama base_url: http://ollama:11434用服务名ollama替代localhost这个在 compose 网络内部自动生效比host.docker.internal更直观且稳定。两种方式我都用过如果不想把 Ollama 也容器化就选host.docker.internal如果愿意两个服务放一个网络里是最整洁的方案。8. 最后一步验证、收尾与长期维护8.1 启停和日常维护部署完以后日常操作只需要记住几个命令docker compose ps # 查看当前状态 docker compose logs -f openclaw # 跟踪日志 docker compose restart openclaw # 重启服务 docker compose down docker compose up -d # 彻底重建镜像更新后旧镜像会残留在本地磁盘定期清理docker system prune -f docker image prune -f8.2 个人长期使用的经验我在 Docker 里跑 OpenClaw 小半年体会最深的一件事是把配置、技能、数据三个目录跟容器彻底解耦之后日常维护其实只需要碰 compose 文件和环境变量所有复杂依赖都锁在镜像里。换机器迁移时压缩三个目录加一个.env文件打包带走新机器上装好 Docker、解包、写 compose、起容器十分钟内恢复全部状态。另一个体会是Token 这种机密信息一定要走环境变量并且.env文件必须加进.gitignore。我有一次差点把.env提交到仓库还好在 push 前发现。这种事在开源项目里发生过太多次Token 泄露后光是被盗刷的 API 费用就够买台新电脑了。如果你打算长期维护这套系统建议一开始就养成习惯每次修改配置文件前先复制一份备份升级前先备份 data 目录日志要认真看而不是只靠猜。这一套流程跑顺之后你会发现容器化部署不是负担反而是解放——你终于不用再伺候那堆乱七八糟的本地环境依赖了。 SEO 优化官网定制响应式建站教育培训建站