OpenClaw Docker 接管实战教程:基于真实迁移成功案例,从旧环境迁移、重装到全新安装一步步跑通
摘要
这是一篇按“照着一步步做就能复现本次成功安装结果”标准整理的实战教程。它不讲虚的,也不假装所有问题都已经被彻底固化,而是基于这次真实跑通、真实验收成功的路径,把 ClawPanel + OpenClaw Gateway 在 Docker 中的 V1 可用方案完整写出来。
本文会明确拆成两条线路:
- 全新安装线路:适合新机器、新目录、从零起步部署的人。
- 重装 / 迁移线路:适合已经有旧 ClawPanel / OpenClaw 环境、想把数据切换到宿主机 bind mount、同时保留原记忆、插件、会话和配置的人。
两条线路都会逐步写清楚:
- 每一步要做什么
- 为什么要这样做
- 要执行的完整命令
- 要创建或修改的完整文件内容
- 每一步执行后应该看到什么
- 如果结果不对,优先怎么排查
本文记录的是本次已经真实跑通的成功路径。它的重点是:
- 成功切换到宿主机 bind mount 数据目录
clawpanel正常运行openclaw-gateway正常运行- gateway
/health正常返回 - webchat 可继续使用
- 微信插件启动链正常
同时也要提前讲清楚:
- 这篇文章写的是当前已经验证成功的方案,不是假装所有后续固化工作都已经完成
- 当前 gateway 依然是运行时在线安装 OpenClaw CLI
- 后续如果还有进一步固化方案,应另外写新文章补充,而不是把未验证完成的内容硬塞进本文
导航
- 一、这篇文章最终要达到什么结果
- 二、先讲清楚:这套 V1 方案到底解决了什么
- 三、部署前准备:无论新装还是重装,都先把这些前提确认清楚
- 四、方案 A:全新安装(从空目录开始)
- 五、方案 B:已有旧环境,重装 / 迁移到 bind mount
- 六、部署完成后的最终验收清单
- 七、常见故障与优先排查顺序
- 八、这套 V1 方案当前的边界
- 九、最终总结
一、这篇文章最终要达到什么结果
按本文操作完成后,你应当能达到下面这组结果:
Docker 项目根目录固定在:
/opt/clawpanel-all-in-oneOpenClaw 持久化数据根目录固定在:
/opt/clawpanel-all-in-one/data/openclawdocker compose ps应看到:clawpanel->Upopenclaw-gateway->Up (healthy)
执行以下健康检查命令:
curl http://127.0.0.1:18789/health应返回:
{"ok":true,"status":"live"}宿主机可以直接看到 OpenClaw 关键数据,例如:
workspace/workspace/MEMORY.mdworkspace/memory/extensions/agents/openclaw.json
Web 端可以继续对话
微信插件启动链正常,不再依赖“数据全藏在 Docker volume 黑盒里”的旧状态
二、先讲清楚:这套 V1 方案到底解决了什么
这次工程里,真正被解决掉的,不是“某次侥幸启动成功”,而是下面这些关键问题:
1. 数据从 Docker volume 黑盒,切到宿主机 bind mount 可见目录
以前最麻烦的问题之一,是 OpenClaw 核心数据主要躺在 Docker volume 里。只要一涉及:
- 备份
- 排障
- 迁移
- 查会话
- 查插件
- 查记忆文件
就很容易变成“先猜 volume 在哪里”。
这次成功路径里,已经把核心数据切到了宿主机可直接查看的目录:
/opt/clawpanel-all-in-one/data/openclaw
这意味着:
- 记忆文件能直接看
- 工作区能直接看
- 插件目录能直接看
- agent 会话能直接看
openclaw.json能直接看- 做备份、迁移、排障时,不再先从 volume 黑盒猜起
2. gateway 启动命令已经验证了正确姿势
本次实战已经明确验证:
正确命令:
openclaw gateway
不要再用:
openclaw gateway start
openclaw gateway start --foreground
之前出过一次大坑,就是 compose 里把 gateway 启动命令写成了 openclaw gateway start --foreground,结果当前版本根本不认,直接把链路搞废。
所以本文后面所有正式写法,都以:
openclaw gateway
为唯一正确前台运行方式。
3. healthcheck 已切到 Node 脚本,不再依赖 curl 假告警
之前还有一个很烦的坑:
- 容器明明其实已经起来了
- 但
docker compose ps里却显示unhealthy - 根因不是 gateway 真挂了,而是 healthcheck 用了
curl node:22-slim镜像里不一定有curl
所以这次成功方案里,healthcheck 改成了:
healthcheck:
test: ["CMD", "node", "/healthcheck/gateway-healthcheck.js"]
对应脚本是:
/opt/clawpanel-all-in-one/runtime/gateway-healthcheck.js
这样就不会再被“curl 不存在”这种低级问题污染健康状态判断。
4. 这篇文章是 V1,不是假装 V2 已完成
这一点必须说清楚,免得误导别人。
本篇写的是:
- 已经跑通的真实方案
- 当前可长期使用的 V1 稳定方案
但它还不是:
- 独立
Dockerfile.gateway固化镜像终版 - 完全不依赖运行时在线安装 CLI 的终极版本
当前 V1 的 gateway 仍然是用下面这种启动逻辑:
npm install -g @qingchencloud/openclaw-zh --registry https://registry.npmmirror.com && \
openclaw init 2>/dev/null || true && \
openclaw gateway
这套逻辑已经被这次实际部署验证为可用,因此 V1 可以发。
未来如果把 gateway 再进一步固化成更干净的镜像方案,那是后续 V2 的内容,不在本文里冒充已经完成。
三、部署前准备:无论新装还是重装,都先把这些前提确认清楚
这一节不要跳。尤其是第一次部署的人,最容易因为“看起来简单”直接跳过去,最后自己把自己坑了。
3.1 机器与系统前提
本文默认你使用的是一台 Linux 服务器,并且:
- 你有 root 权限,或者有等价的 sudo 权限
- 系统已经安装好 Docker
- 系统已经安装好 Docker Compose 插件(即
docker compose可用) - 你能正常访问公网 npm 源,或者能访问文中使用的镜像源
- 你允许把项目根目录放在:
/opt/clawpanel-all-in-one
3.2 先检查 Docker 是否可用
执行下面两条命令:
docker --version
docker compose version
预期结果
两条命令都应该正常输出版本号,例如:
Docker version 27.x.x, build xxxxx
Docker Compose version v2.x.x
如果不对怎么办
- 如果
docker --version报错:先安装 Docker - 如果
docker compose version报错:先安装 Docker Compose 插件 - 这两项没准备好,后面的内容不要继续
3.3 本文统一使用的目录结构
本文统一使用:
/opt/clawpanel-all-in-one/
├── Dockerfile.clawpanel
├── docker-compose.yml
├── runtime/
│ └── gateway-healthcheck.js
├── scripts/
│ ├── backup-pre-reinstall.sh
│ ├── migrate-openclaw-data.sh
│ └── verify-openclaw.sh
└── data/
└── openclaw/
各目录的作用
/opt/clawpanel-all-in-one/- 项目总根目录
/opt/clawpanel-all-in-one/data/openclaw/- OpenClaw 的持久化数据目录
/opt/clawpanel-all-in-one/runtime/- 放运行时辅助脚本,例如 healthcheck
/opt/clawpanel-all-in-one/scripts/- 放备份、迁移、验收等运维脚本
3.4 当前 V1 的核心思路
本文不是“两个容器都自己维护自己的数据”。
当前 V1 的核心设计是:
clawpanel使用项目内构建出来的镜像openclaw-gateway使用node:22-slim- gateway 启动时在线安装
@qingchencloud/openclaw-zh - 两个容器都把
/root/.openclaw挂到宿主机同一个目录:/opt/clawpanel-all-in-one/data/openclaw
这样做的直接结果是:
- 数据统一
- 宿主机可见
- backup / migrate / verify 的逻辑统一
- 当前这轮真实成功结果可复现
四、方案 A:全新安装(从空目录开始)
这一节适合:
- 新服务器
- 新目录
- 之前没有现成旧环境
- 或者你明确接受从零开始搭一套新的结构
A-1. 创建项目目录
这一步的目的
在宿主机上创建统一的部署根目录和子目录,后面所有文件都放这里,不要边做边换地方。
要执行的命令
mkdir -p /opt/clawpanel-all-in-one
mkdir -p /opt/clawpanel-all-in-one/runtime
mkdir -p /opt/clawpanel-all-in-one/scripts
mkdir -p /opt/clawpanel-all-in-one/data/openclaw
cd /opt/clawpanel-all-in-one
pwd
预期结果
最后一条 pwd 应输出:
/opt/clawpanel-all-in-one
如果不对怎么办
- 如果提示权限不足,请使用 root 或 sudo
- 如果你不打算用本文的固定路径,就不要继续照抄本文,后面所有路径都得跟着改
A-2. 创建 Dockerfile.clawpanel
这一步的目的
构建 clawpanel 容器镜像。当前这次成功方案里,clawpanel 侧使用的是单独 Dockerfile。
要执行的命令
cat > /opt/clawpanel-all-in-one/Dockerfile.clawpanel <<'EOF'
FROM node:22-slim
RUN apt-get update && apt-get install -y python3 git ca-certificates && rm -rf /var/lib/apt/lists/*
WORKDIR /app
RUN git clone https://github.com/qingchencloud/clawpanel.git . && npm install
RUN npm run build
EXPOSE 1420
CMD ["npm", "run", "serve"]
EOF
写完后立即检查文件内容
执行:
sed -n '1,200p' /opt/clawpanel-all-in-one/Dockerfile.clawpanel
你应该看到的完整内容
FROM node:22-slim
RUN apt-get update && apt-get install -y python3 git ca-certificates && rm -rf /var/lib/apt/lists/*
WORKDIR /app
RUN git clone https://github.com/qingchencloud/clawpanel.git . && npm install
RUN npm run build
EXPOSE 1420
CMD ["npm", "run", "serve"]
如果不对怎么办
- 如果文件里混进了 shell 提示符、额外空行、命令本身文字,说明你粘贴脏了,删掉重写
- 如果
sed看不到完整内容,先别继续 build
A-3. 创建 gateway Node healthcheck 脚本
这一步的目的
给 openclaw-gateway 提供一个不依赖 curl 的健康检查脚本,避免 node:22-slim 环境里因为缺少 curl 导致误报 unhealthy。
要执行的命令
cat > /opt/clawpanel-all-in-one/runtime/gateway-healthcheck.js <<'EOF'
const url = 'http://127.0.0.1:18789/health';
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 4000);
fetch(url, { signal: controller.signal })
.then(async (res) => {
clearTimeout(timeout);
const text = await res.text();
if (!res.ok) {
console.error(`healthcheck failed: status=${res.status} body=${text}`);
process.exit(1);
}
process.stdout.write(text);
process.exit(0);
})
.catch((err) => {
clearTimeout(timeout);
console.error(`healthcheck error: ${err.message}`);
process.exit(1);
});
EOF
写完后立即检查
sed -n '1,200p' /opt/clawpanel-all-in-one/runtime/gateway-healthcheck.js
你应该看到的完整内容
const url = 'http://127.0.0.1:18789/health';
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 4000);
fetch(url, { signal: controller.signal })
.then(async (res) => {
clearTimeout(timeout);
const text = await res.text();
if (!res.ok) {
console.error(`healthcheck failed: status=${res.status} body=${text}`);
process.exit(1);
}
process.stdout.write(text);
process.exit(0);
})
.catch((err) => {
clearTimeout(timeout);
console.error(`healthcheck error: ${err.message}`);
process.exit(1);
});
如果不对怎么办
- 少一行都别往下走
fetch、AbortController、process.exit这些关键逻辑不要擅自改
A-4. 创建验收脚本 scripts/verify-openclaw.sh
这一步的目的
把部署后的检查固定成一个脚本,不要每次靠肉眼想“我该查什么”。
要执行的命令
cat > /opt/clawpanel-all-in-one/scripts/verify-openclaw.sh <<'EOF'
#!/usr/bin/env bash
set -eu
cd /opt/clawpanel-all-in-one
echo "==> [1/4] docker compose ps"
docker compose ps
echo
echo "==> [2/4] gateway /health"
HEALTH_OUTPUT="$(curl -fsS http://127.0.0.1:18789/health)"
echo "$HEALTH_OUTPUT"
if [ "$HEALTH_OUTPUT" != '{"ok":true,"status":"live"}' ]; then
echo "❌ gateway /health 返回异常"
exit 1
fi
echo
echo "==> [3/4] data/openclaw top level"
find /opt/clawpanel-all-in-one/data/openclaw -maxdepth 2 -mindepth 1 | sort | sed -n '1,120p'
echo
echo "==> [4/4] done"
echo "✅ 基础巡检通过:compose / health / data 目录均已检查"
EOF
赋予执行权限
chmod +x /opt/clawpanel-all-in-one/scripts/verify-openclaw.sh
检查文件内容
sed -n '1,200p' /opt/clawpanel-all-in-one/scripts/verify-openclaw.sh
你应该看到的完整内容
#!/usr/bin/env bash
set -eu
cd /opt/clawpanel-all-in-one
echo "==> [1/4] docker compose ps"
docker compose ps
echo
echo "==> [2/4] gateway /health"
HEALTH_OUTPUT="$(curl -fsS http://127.0.0.1:18789/health)"
echo "$HEALTH_OUTPUT"
if [ "$HEALTH_OUTPUT" != '{"ok":true,"status":"live"}' ]; then
echo "❌ gateway /health 返回异常"
exit 1
fi
echo
echo "==> [3/4] data/openclaw top level"
find /opt/clawpanel-all-in-one/data/openclaw -maxdepth 2 -mindepth 1 | sort | sed -n '1,120p'
echo
echo "==> [4/4] done"
echo "✅ 基础巡检通过:compose / health / data 目录均已检查"
说明
这个 V1 验收脚本做的是“基础验收”,重点检查:
- compose 状态
- gateway
/health - bind mount 数据目录
如果你后面还要加更复杂的插件状态检查,可以在这个脚本基础上继续增强。但本文先保证当前真实成功基线能复现。
A-5. 创建迁移脚本 scripts/migrate-openclaw-data.sh
这一步的目的
即使你现在是全新安装,也建议把迁移脚本先放好。这样后面如果要从旧 volume 导数据,脚本已经在位。
要执行的命令
cat > /opt/clawpanel-all-in-one/scripts/migrate-openclaw-data.sh <<'EOF'
#!/usr/bin/env bash
set -eu
OLD_VOLUME="${1:-clawpanel-all-in-one_openclaw-data}"
TARGET_DIR="/opt/clawpanel-all-in-one/data/openclaw"
mkdir -p "$TARGET_DIR"
echo "==> source volume: $OLD_VOLUME"
echo "==> target dir : $TARGET_DIR"
docker run --rm \
-v "$OLD_VOLUME":/from \
-v "$TARGET_DIR":/to \
alpine:3.20 \
sh -c 'cd /from && cp -a . /to/'
echo "✅ migration copy finished"
EOF
赋予执行权限
chmod +x /opt/clawpanel-all-in-one/scripts/migrate-openclaw-data.sh
立即检查
sed -n '1,200p' /opt/clawpanel-all-in-one/scripts/migrate-openclaw-data.sh
A-6. 创建重装前备份脚本 scripts/backup-pre-reinstall.sh
这一步的目的
把重装前备份动作固定下来,避免真正到重装那一步时才手忙脚乱。
要执行的命令
cat > /opt/clawpanel-all-in-one/scripts/backup-pre-reinstall.sh <<'EOF'
#!/usr/bin/env bash
set -eu
TS="$(date -u +%Y%m%dT%H%M%SZ)"
BACKUP_ROOT="/opt/clawpanel-all-in-one/backup/pre-reinstall-$TS"
mkdir -p "$BACKUP_ROOT"
if [ -f /opt/clawpanel-all-in-one/docker-compose.yml ]; then
cp -a /opt/clawpanel-all-in-one/docker-compose.yml "$BACKUP_ROOT/"
fi
if [ -f /opt/clawpanel-all-in-one/Dockerfile.clawpanel ]; then
cp -a /opt/clawpanel-all-in-one/Dockerfile.clawpanel "$BACKUP_ROOT/"
fi
if [ -d /opt/clawpanel-all-in-one/runtime ]; then
cp -a /opt/clawpanel-all-in-one/runtime "$BACKUP_ROOT/"
fi
if [ -d /opt/clawpanel-all-in-one/scripts ]; then
cp -a /opt/clawpanel-all-in-one/scripts "$BACKUP_ROOT/"
fi
if [ -d /opt/clawpanel-all-in-one/data/openclaw ]; then
tar -C /opt/clawpanel-all-in-one/data -czf "$BACKUP_ROOT/openclaw-data.tar.gz" openclaw
fi
echo "✅ backup done: $BACKUP_ROOT"
EOF
赋予执行权限
chmod +x /opt/clawpanel-all-in-one/scripts/backup-pre-reinstall.sh
立即检查
sed -n '1,220p' /opt/clawpanel-all-in-one/scripts/backup-pre-reinstall.sh
A-7. 创建 docker-compose.yml
这一步的目的
把整个部署结构写死下来:
clawpanel单独 buildopenclaw-gateway使用node:22-slim/root/.openclaw统一挂到宿主机 bind mount- healthcheck 使用 Node 脚本
- gateway 使用已验证正确的前台命令
openclaw gateway
要执行的命令
cat > /opt/clawpanel-all-in-one/docker-compose.yml <<'EOF'
services:
gateway:
image: node:22-slim
container_name: openclaw-gateway
restart: unless-stopped
working_dir: /root
command: >
sh -c "npm install -g @qingchencloud/openclaw-zh --registry https://registry.npmmirror.com &&
openclaw init 2>/dev/null || true &&
openclaw gateway"
ports:
- "18789:18789"
volumes:
- ./data/openclaw:/root/.openclaw
- ./runtime/gateway-healthcheck.js:/healthcheck/gateway-healthcheck.js:ro
healthcheck:
test: ["CMD", "node", "/healthcheck/gateway-healthcheck.js"]
interval: 30s
timeout: 5s
retries: 5
start_period: 40s
clawpanel:
build:
context: .
dockerfile: Dockerfile.clawpanel
container_name: clawpanel
restart: unless-stopped
working_dir: /app
ports:
- "1420:1420"
volumes:
- ./data/openclaw:/root/.openclaw
environment:
NODE_ENV: production
depends_on:
gateway:
condition: service_healthy
EOF
写完后立即检查
sed -n '1,240p' /opt/clawpanel-all-in-one/docker-compose.yml
你应该看到的完整内容
services:
gateway:
image: node:22-slim
container_name: openclaw-gateway
restart: unless-stopped
working_dir: /root
command: >
sh -c "npm install -g @qingchencloud/openclaw-zh --registry https://registry.npmmirror.com &&
openclaw init 2>/dev/null || true &&
openclaw gateway"
ports:
- "18789:18789"
volumes:
- ./data/openclaw:/root/.openclaw
- ./runtime/gateway-healthcheck.js:/healthcheck/gateway-healthcheck.js:ro
healthcheck:
test: ["CMD", "node", "/healthcheck/gateway-healthcheck.js"]
interval: 30s
timeout: 5s
retries: 5
start_period: 40s
clawpanel:
build:
context: .
dockerfile: Dockerfile.clawpanel
container_name: clawpanel
restart: unless-stopped
working_dir: /app
ports:
- "1420:1420"
volumes:
- ./data/openclaw:/root/.openclaw
environment:
NODE_ENV: production
depends_on:
gateway:
condition: service_healthy
这一步特别注意
不要把 gateway command 写成:
openclaw gateway start
也不要写成:
openclaw gateway start --foreground
这两个都不是本次成功路径。
A-8. 构建并启动服务
这一步的目的
正式把 clawpanel 和 openclaw-gateway 拉起来。
要执行的命令
cd /opt/clawpanel-all-in-one
docker compose up -d --build
第一次启动时要有心理准备
第一次启动通常会比普通容器慢,原因包括:
clawpanel要 build- gateway 首次启动时会在线安装
@qingchencloud/openclaw-zh - 健康检查要等服务真正起来后才会转绿
查看启动过程
建议马上执行:
docker compose ps
docker compose logs --tail=100 gateway
docker compose logs --tail=100 clawpanel
预期结果
你最终应看到:
gateway容器存在clawpanel容器存在- gateway 日志里出现类似:
heartbeat startedhealth-monitor startedlistening on ws://127.0.0.1:18789
- clawpanel 日志里出现类似:
ClawPanel Web Server (Headless)http://0.0.0.0:1420/
如果不对怎么办
- 如果 gateway 容器反复退出:
先查
docker compose logs gateway - 如果看到和
openclaw gateway start --foreground有关的报错: 说明你命令写错了 - 如果
clawpanel没起来: 查docker compose logs clawpanel - 如果
gateway长时间starting: 多半是 CLI 在线安装还没跑完,先别急着判死刑
A-9. 基础验收
这一步的目的
确认整个结构已经真正跑起来,而不是“看起来大概差不多”。
先跑验收脚本
cd /opt/clawpanel-all-in-one
./scripts/verify-openclaw.sh
再手工补两条关键检查
docker compose ps
curl -fsS http://127.0.0.1:18789/health
预期结果
docker compose ps里看到:clawpanel->Upopenclaw-gateway->Up (healthy)
curl -fsS http://127.0.0.1:18789/health返回:
{"ok":true,"status":"live"}
再检查宿主机数据目录
find /opt/clawpanel-all-in-one/data/openclaw -maxdepth 2 -mindepth 1 | sort | sed -n '1,120p'
预期结果
你应该能在宿主机上直接看到 /root/.openclaw 的映射内容,而不是一个空目录。
在全新安装场景下,一开始内容可能还不多,但目录至少不该完全没有 OpenClaw 运行痕迹。
五、方案 B:已有旧环境,重装 / 迁移到 bind mount
这一节是本文最重要的部分之一。
它适合下面这些场景:
- 你之前已经跑过 ClawPanel / OpenClaw
- 数据还在旧 Docker volume 里
- 你不想丢失记忆、插件、会话、配置
- 你想把旧结构迁到宿主机 bind mount
- 同时保留一个可回滚的旧保险
B-1. 先确认你现在是不是旧环境迁移场景
如果你满足下面任意一条,就优先走本节,而不是“当自己是新装”:
- 旧环境里已经有记忆文件
- 旧环境里已经登录过微信插件
- 旧环境里已有 agent 会话记录
- 旧环境里已经存在
openclaw.json - 你担心重装后数据丢失
只要你属于这些情况之一,就不要偷懒,不要跳过备份和卷名确认步骤。
B-2. 停掉当前服务
这一步的目的
在真正做迁移前,先停止旧容器,避免迁移时数据仍在被写入。
要执行的命令
cd /opt/clawpanel-all-in-one
docker compose down
预期结果
容器被停止并移除,但 volume 不会因为这一步自动删除。
注意
不要执行这种带卷删除的命令:
docker compose down -v
这条在没确认备份和迁移前,风险太高。
B-3. 确认真实旧 volume 名,不要想当然
这一步的目的
本次实战已经踩过坑:
真实旧卷名是:
clawpanel-all-in-one_openclaw-data
不是:
openclaw-data
但你在自己机器上仍然应该先确认,不要因为文章里写了一个名字,就直接拿去赌。
先列出本机 volume
docker volume ls
再定位当前项目实际使用的卷
docker inspect clawpanel --format '{{json .Mounts}}'
docker inspect openclaw-gateway --format '{{json .Mounts}}'
你应该重点看什么
看输出里是否出现:
Type: volume- 卷名接近
clawpanel-all-in-one_openclaw-data - 目标挂载路径是
/root/.openclaw
本次实战确认的结论
这次真实成功迁移里,旧卷名已实锤为:
clawpanel-all-in-one_openclaw-data
后面示例如果没特别说明,都按这个真实卷名演示。
B-4. 先做重装前备份
这一步的目的
在真正迁移前,先把当前 compose、脚本、运行目录和 bind mount 数据(如果已有)打一个时间戳备份。
要执行的命令
cd /opt/clawpanel-all-in-one
./scripts/backup-pre-reinstall.sh
预期结果
脚本应输出类似:
✅ backup done: /opt/clawpanel-all-in-one/backup/pre-reinstall-20260328T090000Z
备份完成后立即检查
find /opt/clawpanel-all-in-one/backup -maxdepth 2 -mindepth 1 | sort | tail -n 20
说明
即使你觉得“反正旧 volume 还在”,这一步也不要省。多一层保险总比真出事后哭强。
B-5. 创建 bind mount 目标目录
这一步的目的
为迁移后的宿主机数据目录做好承接位置。
要执行的命令
mkdir -p /opt/clawpanel-all-in-one/data/openclaw
ls -ld /opt/clawpanel-all-in-one/data/openclaw
预期结果
应能看到目录存在。
注意
- 如果目录已经存在,不是问题
- 真正危险的是:你以为存在,实际路径写错了
B-6. 把旧 volume 数据复制到宿主机 bind mount 目录
这一步的目的
把旧的 /root/.openclaw 数据从 Docker volume 拷出来,复制到宿主机目录 /opt/clawpanel-all-in-one/data/openclaw。
要执行的命令
cd /opt/clawpanel-all-in-one
./scripts/migrate-openclaw-data.sh clawpanel-all-in-one_openclaw-data
如果你已经核对出自己机器的旧卷名不同
那就把最后一个参数替换成你实际查出来的卷名,不要死抄:
./scripts/migrate-openclaw-data.sh 你的真实旧卷名
预期结果
脚本应输出类似:
==> source volume: clawpanel-all-in-one_openclaw-data
==> target dir : /opt/clawpanel-all-in-one/data/openclaw
✅ migration copy finished
迁移完成后立即检查目标目录
find /opt/clawpanel-all-in-one/data/openclaw -maxdepth 2 -mindepth 1 | sort | sed -n '1,120p'
你应该看到什么
至少应看到大量原本属于 /root/.openclaw 的数据,例如:
workspace/extensions/agents/openclaw.json
如果目标目录还是空的怎么办
优先排查:
- 你传入的旧卷名是不是错了
- 旧容器真实挂载的卷名是不是另一个名字
docker run -v 旧卷:/from ...是否能实际读到内容
不要在这一步目录还是空的时候直接继续 up -d。那样很容易把“迁移失败”伪装成“新环境覆盖旧环境”。
B-7. 写入正式的 V1 docker-compose.yml
如果你前面已经按方案 A 写好了 docker-compose.yml,这里仍然建议再核对一遍,确保你真正用的是 bind mount 版本,而不是旧的 volume 版本。
再次确认 compose 文件中的关键行
执行:
grep -n "./data/openclaw:/root/.openclaw" /opt/clawpanel-all-in-one/docker-compose.yml
grep -n "gateway-healthcheck.js" /opt/clawpanel-all-in-one/docker-compose.yml
grep -n "openclaw gateway" /opt/clawpanel-all-in-one/docker-compose.yml
预期结果
应至少命中下面这些核心特征:
./data/openclaw:/root/.openclaw./runtime/gateway-healthcheck.js:/healthcheck/gateway-healthcheck.js:roopenclaw gateway
如果 grep 没命中怎么办
不要继续启动,先回去把 compose 改对。
B-8. 启动新结构
要执行的命令
cd /opt/clawpanel-all-in-one
docker compose up -d --build
启动后立刻看状态
docker compose ps
docker compose logs --tail=100 gateway
docker compose logs --tail=100 clawpanel
预期结果
gateway起得来clawpanel起得来- gateway 后续应转为
healthy - 旧数据应被新结构正确使用,而不是变成一套空白的新环境
B-9. 验证旧数据确实被新结构接住了
要执行的命令
./scripts/verify-openclaw.sh
再补充执行:
find /opt/clawpanel-all-in-one/data/openclaw/workspace -maxdepth 2 -mindepth 1 | sort | sed -n '1,120p'
find /opt/clawpanel-all-in-one/data/openclaw/extensions -maxdepth 2 -mindepth 1 | sort | sed -n '1,120p'
find /opt/clawpanel-all-in-one/data/openclaw/agents -maxdepth 2 -mindepth 1 | sort | sed -n '1,120p'
ls -l /opt/clawpanel-all-in-one/data/openclaw/openclaw.json
预期结果
你应该在宿主机上直接看到:
- 原来的工作区文件
- 记忆文件
- 扩展插件目录
- agent 会话目录
openclaw.json
这一步如果成立,才说明本次迁移真正成功了。
B-10. 保留旧 volume,不要急着删
这一步的目的
给自己留回滚锚点。
本次实战的明确结论
旧 volume:
clawpanel-all-in-one_openclaw-data
目前建议保留,不要删。
为什么不要删
因为新 bind mount 结构虽然已经实测跑通,但观察期内保留旧卷,能给你这些保障:
- 万一发现遗漏数据,还能回查
- 万一新结构后面又出问题,还有额外保险
- 可以做二次离线导出
当前建议
至少等新结构稳定运行一段时间后,再考虑是否清理旧卷。
六、部署完成后的最终验收清单
这一节无论是新装还是重装,都必须做。
6.1 容器状态验收
执行:
cd /opt/clawpanel-all-in-one
docker compose ps
通过标准
应该看到:
clawpanel->Upopenclaw-gateway->Up (healthy)
如果 gateway 不是 healthy,先别自我安慰说“应该没事”,先往下查。
6.2 gateway 健康检查验收
执行:
curl -fsS http://127.0.0.1:18789/health
通过标准
必须返回:
{"ok":true,"status":"live"}
6.3 gateway 日志特征验收
执行:
docker compose logs --tail=200 gateway
通过标准
应能看到类似信号:
heartbeat startedhealth-monitor startedagent model:listening on ws://127.0.0.1:18789openclaw-weixin相关启动日志weixin monitor started
6.4 clawpanel 日志特征验收
执行:
docker compose logs --tail=200 clawpanel
通过标准
应能看到类似:
ClawPanel Web Server (Headless)http://0.0.0.0:1420/配置目录: /root/.openclaw
6.5 数据目录验收
执行:
find /opt/clawpanel-all-in-one/data/openclaw -maxdepth 2 -mindepth 1 | sort | sed -n '1,120p'
通过标准
宿主机上应能直接看到 OpenClaw 关键数据,而不是空目录。
重装场景里尤其要确认:
workspace/workspace/MEMORY.mdworkspace/memory/extensions/agents/openclaw.json
6.6 业务侧验收
最后再做人类视角的业务验收:
- 打开 webchat,看是否能正常对话
- 检查微信插件是否正常工作
- 检查原有记忆 / 配置 / 会话是否仍可继续使用
这一步不能省,因为“容器起来了”不等于“业务真的可用了”。
七、常见故障与优先排查顺序
7.1 gateway 显示 unhealthy
先执行:
docker compose ps
docker compose logs --tail=200 gateway
curl -fsS http://127.0.0.1:18789/health
怎么判断
- 如果
/health已经返回{"ok":true,"status":"live"}但 compose 仍显示unhealthy才去怀疑 healthcheck 本身 - 但本文这套 V1 已经把 healthcheck 切到 Node 脚本,正常情况下不该再因为
curl缺失误报
7.2 gateway 启动失败
先看 compose 里是不是把命令写错了。
正确:
openclaw gateway
错误:
openclaw gateway start
openclaw gateway start --foreground
这两个错误命令不要再碰。
7.3 迁移后 data/openclaw 还是空的
优先怀疑:
- 旧卷名写错
- 没有先 inspect 真实挂载
- 复制时其实没从正确的卷里读到数据
本次实战的关键坑就是:
- 真正旧卷是
clawpanel-all-in-one_openclaw-data - 不是很多人会想当然写的
openclaw-data
7.4 重装后看起来像全新环境
这通常意味着:
- bind mount 目录没接到原数据
- 或者你启动时绑定的是一个空目录
不要继续盲目登录、盲目初始化,先查宿主机 data/openclaw 里到底有没有旧数据。
八、这套 V1 方案当前的边界
本文是当前真实成功路径的完整沉淀,但也要诚实说清边界:
- 当前 gateway 仍是运行时在线安装 CLI,不是最终镜像固化版
- 本文解决的是“本次真实可复现成功路径”,不是宣称未来所有版本、所有插件行为永远不变
- 首次接入外部平台(例如某些插件登录、扫码、授权)仍可能需要人工参与
- 如果你未来要继续追求更强固化,下一步方向应是:
- 评估单独
Dockerfile.gateway - 在镜像构建阶段预装 OpenClaw CLI
- 进一步减少运行时在线安装依赖
- 评估单独
但这些属于 V2,不是本文要硬塞进去假装已经做完的内容。
九、最终总结
如果你要的是一篇:
- 不省关键步骤
- 区分新装和重装
- 命令能直接抄
- 文件内容完整给出
- 能按步骤复现本次真实成功结果
- 并且能把数据切到宿主机 bind mount、让 webchat / gateway / 微信插件链路继续正常工作的教程
那么本文这版 V1,就是当前应该使用的版本。
最后把最关键的事实再压一遍:
当前 V1 成功基线
- 项目根目录:
/opt/clawpanel-all-in-one - 数据根目录:
/opt/clawpanel-all-in-one/data/openclaw - gateway 正确命令:
openclaw gateway - healthcheck: Node 脚本,不用 curl 探测
- 旧卷真实名字:
clawpanel-all-in-one_openclaw-data - 旧卷当前建议: 先保留,不要急着删
当前推荐的日常检查顺序
cd /opt/clawpanel-all-in-one
./scripts/verify-openclaw.sh
如果还要再补:
docker compose ps
curl -fsS http://127.0.0.1:18789/health
做到这里,你就不是“这次凑巧跑通”,而是已经把一套可复现、可排查、可回滚、可继续维护的 V1 路线真正搭起来了。
标签建议
- OpenClaw
- ClawPanel
- Docker
- Docker Compose
- 微信插件
- 部署教程
- 运维实战
- 数据迁移
SEO 描述
这是一篇基于真实成功路径整理的 ClawPanel + OpenClaw Gateway Docker 一体化部署实战教程,完整覆盖全新安装、旧环境重装迁移、bind mount 数据切换、gateway 健康检查、验收脚本与常见故障排查,适合希望按步骤复现可用状态的新手用户。
评论区