备份和恢复
单机部署模式下,FinClaw 的所有有状态数据都在 Docker 命名卷里(
chatkit_*前缀)。本节给出一种最简单可行的备份方案:停服 → 整卷打包 → 启服。生产环境如需 RPO/RTO 更严格的方案,可再叠加pg_dump逻辑备份或卷快照。
一、需要备份的内容
1. 配置文件
| 路径 | 说明 |
|---|---|
deploy/.env | LLM 凭证、JWT 密钥、内部 token、并发参数 |
deploy/config/ | services.yaml、ai-infra.yaml、idp.yaml 等 yaml 配置 |
deploy/docker-compose.override.yml(如有) | 自定义端口等覆写 |
2. Docker 数据卷
chatkit_* 系列卷,全部由 docker-compose.yml 声明为 external: true:
| 卷名 | 内容 |
|---|---|
chatkit_postgres_data | PostgreSQL 业务数据(用户/租户/画像/技能/收件箱/文档元数据,及飞书、钉钉、企微 connector 数据) |
chatkit_timescaledb_data | TimescaleDB 数据(conversation-store 会话历史与审计数据) |
chatkit_ai_infra_data | AI 运行时持久化数据(审计日志、记忆索引、会话状态、MCP servers 配置、fleet 运行时状态) |
chatkit_workspaces | 用户工作区(文件、记忆文件、已安装技能) |
chatkit_skills_data | 平台技能数据(builtin + catalog) |
chatkit_persona_uploads | 画像服务上传文件 |
chatkit_identity_uploads | Identity 服务上传文件(头像等) |
chatkit_doc_toolkit_cache | 文档工具缓存(纯缓存,理论上可不备份;按「全打包」原则一并备份最稳妥) |
chatkit_nats_data | NATS JetStream(事件流,短期消息) |
chatkit_redis_data | Redis 缓存与协调状态 |
chatkit_restate_data | Restate 定时任务 / cron 状态 |
chatkit_tei_models | TEI 向量模型文件(离线环境无法重新下载模型,必须备份) |
单机部署的简单原则:
chatkit_*全打包 —— 不要试图人工筛选,漏掉一个就可能丢数据。
二、备份操作
步骤 1:停服
数据卷在容器运行期备份会得到不一致快照(特别是 PostgreSQL)。先停服再备份。
cd /data/chatkit-offline-release-1.1.20-linux-amd64/deploy # 按实际版本替换
./scripts/fleet.sh down
down会停止全部容器(含 per-tenant 租户 Claw 容器),但不会删除任何数据卷。
步骤 2:打包配置 + 数据卷
⚠️ 离线环境注意:打包/解包用的一次性容器必须使用离线包自带的镜像(见
manifests/basic-images.list),不要写alpine、busybox等需要联网拉取的外部镜像。本文统一使用postgres:16-alpine——部署过本套服务的机器上必定存在。
# 准备备份目录
BACKUP_DIR=/data/backups/finclaw-$(date +%Y%m%d-%H%M%S)
mkdir -p "$BACKUP_DIR"
# 1) 配置:直接 tar 打包 deploy 目录里的关键文件
tar -czf "$BACKUP_DIR/deploy-config.tar.gz" -C /data/chatkit-offline-release-1.1.20-linux-amd64/deploy .env config docker-compose.override.yml 2>/dev/null || true
# 2) 确认 helper 镜像存在(部署过本套服务则必定存在,正常无输出即为 OK)
docker image inspect postgres:16-alpine >/dev/null && echo "helper image OK"
# 3) 数据卷:用一次性容器把每个卷的内容打包(tar 保留文件属主)
for vol in $(docker volume ls -q | grep '^chatkit_'); do
echo ">> backing up volume: $vol"
docker run --rm \
-v "$vol":/source:ro \
-v "$BACKUP_DIR":/backup \
postgres:16-alpine \
tar -czf "/backup/${vol}.tar.gz" -C /source .
done
# 4) 记录卷清单 + 版本信息
docker volume ls --filter "name=chatkit_" --format '{{.Name}}' > "$BACKUP_DIR/volumes.txt"
echo "release_dir=$(realpath /data/chatkit-offline-release-1.1.20-linux-amd64)" > "$BACKUP_DIR/meta.txt"
date -u +"backup_time=%Y-%m-%dT%H:%M:%SZ" >> "$BACKUP_DIR/meta.txt"
# 5) 校验并查看备份目录(备份数应与卷清单行数一致)
ls "$BACKUP_DIR"/chatkit_*.tar.gz | wc -l
cat "$BACKUP_DIR/volumes.txt" | wc -l
ls -lh "$BACKUP_DIR"
du -sh "$BACKUP_DIR"全新部署为 12 个卷;从旧版本升级上来的机器可能残留少量已退役的历史卷(无容器挂载),一并打包无害。
备选方案(不起容器):在宿主机以 root 直接打包卷目录也可,无需任何镜像。先用 docker volume inspect 取卷的真实路径(与 Docker 数据目录配置无关),再对卷目录打 tar 包:
# 备份:解析卷路径并打包
tar -czf <包路径> -C $(docker volume inspect -f '{{.Mountpoint}}' <卷名>) .
# 恢复(对称):先建卷再解包
docker volume create <卷名>
tar -xzf <包路径> -C $(docker volume inspect -f '{{.Mountpoint}}' <卷名>)缺点是需要宿主机 root 权限(/var/lib/docker 目录仅 root 可读)。
步骤 3:启服
./scripts/fleet.sh up -d
./scripts/doctor.sh备份产物示例
/data/backups/finclaw-20260625-030000/
├── deploy-config.tar.gz # .env + config/ + override
├── chatkit_postgres_data.tar.gz
├── chatkit_timescaledb_data.tar.gz
├── chatkit_ai_infra_data.tar.gz
├── chatkit_workspaces.tar.gz
├── chatkit_skills_data.tar.gz
├── chatkit_persona_uploads.tar.gz
├── chatkit_identity_uploads.tar.gz
├── chatkit_doc_toolkit_cache.tar.gz
├── chatkit_nats_data.tar.gz
├── chatkit_redis_data.tar.gz
├── chatkit_restate_data.tar.gz
├── chatkit_tei_models.tar.gz
├── volumes.txt # 卷名清单
└── meta.txt # 版本与备份时间戳三、恢复操作
步骤 1:停服并清空旧卷
cd /data/chatkit-offline-release-1.1.20-linux-amd64/deploy # 按实际版本替换
./scripts/fleet.sh down
# 删除所有 chatkit_* 卷(确认要回滚到备份状态再执行!)
docker volume ls -q | grep '^chatkit_' | xargs -r docker volume rm⚠️ 这步会丢失当前未备份的数据,确认无误再执行。
步骤 2:恢复配置
BACKUP_DIR=/data/backups/finclaw-20260625-030000 # 替换为你的备份目录
tar -xzf "$BACKUP_DIR/deploy-config.tar.gz" -C /data/chatkit-offline-release-1.1.20-linux-amd64/deploy⚠️
.env与数据卷必须配套恢复:USER_SECRETS_KEK(用户密钥加密密钥)与加密的用户数据绑定,ADMIN_EMAILS决定哪些账号保有管理员身份。始终使用与数据卷同时备份的那份.env;用重新init-env.sh初始化的.env搭配旧数据卷,加密的用户数据将无法解密。
步骤 3:恢复数据卷
# 逐个还原卷:先创建空卷,再把 tar.gz 解到卷里
# (一次性容器用 postgres:16-alpine,离线环境无需联网)
for tarball in "$BACKUP_DIR"/chatkit_*.tar.gz; do
vol=$(basename "$tarball" .tar.gz)
echo "<< restoring volume: $vol"
docker volume create "$vol" >/dev/null
docker run --rm \
-v "$vol":/target \
-v "$BACKUP_DIR":/backup:ro \
postgres:16-alpine \
tar -xzf "/backup/${vol}.tar.gz" -C /target
done
# 检查:卷数量应与备份清单一致
docker volume ls --filter "name=chatkit_" --format '{{.Name}}' | wc -l步骤 4:启服 + 验证
./scripts/fleet.sh up -d
./scripts/doctor.sh
curl http://127.0.0.1:26100/health预期 doctor.sh 全 ✅、/health 返回 200 OK。若个别服务首次启动较慢,等 2–5 分钟再跑一次。up -d 会在平台启动后自动重建租户 Claw 容器;若未重建,手动执行 ./scripts/reconcile-claw-deployments.sh --force。
