迁移 Linux 主机时,可能只带走了备份目录,没有带走原来的 Duplicati 配置卷。这个场景要核对的是备份文件和加密口令能否读出旧版。下面在一台 Linux 主机上用两个独立的 Compose 项目演练。第一个项目留下同一份文件的 A、B 两个版本,第二个项目用全新的配置卷,从备份目录的副本恢复 A 版。示例固定 Duplicati 2.4.1.0。
先分清要保留的两种数据
/data 存应用设置、任务和本地数据库。备份任务的存储目标另外指向 /backups,保存用于恢复的备份文件。直接恢复需要完整的备份数据和备份加密口令。设置加密密钥保护原实例中的设置,网页登录密码用于进入实例,这两项与备份加密口令各有用途。
同一个 Compose 文件可以让两个项目各自创建命名卷。演练时先停掉原项目,再启动恢复项目。两边都只绑定主机回环地址。
建原项目,留下两个备份点
在装有 Docker Compose v2 和 openssl 的 Linux 主机执行。下列步骤使用新建的目录和演示文件。后续命令都回到该目录运行,并在同一终端保留 project 变量。
demo_dir=$(mktemp -d "$PWD/dup-restore-XXXXXX") &&
cd "$demo_dir" &&
project=$(basename "$PWD" | tr '[:upper:]' '[:lower:]') &&
mkdir source backup-store backup-copy restored &&
printf '%s\n' 'deploy notes A' > source/deploy-notes.txt
确认命令全部成功后,再生成两套环境文件。两份网页登录密码和设置加密密钥分别生成,权限限制为当前用户读写。创建备份任务时还会在网页里设置单独的备份加密口令。
write_env() (
umask 077
key=$(openssl rand -hex 32) &&
password=$(openssl rand -hex 24) &&
printf 'SETTINGS_ENCRYPTION_KEY=%s\nDUPLICATI__WEBSERVICE_PASSWORD=%s\nDUPLICATI_BACKUP_DIR=%s\n' \
"$key" "$password" "$2" > "$1"
)
write_env .env.backup ./backup-store &&
write_env .env.recovery ./backup-copy
将以下内容保存为 compose.yaml。两个项目的 /data 卷按项目名分别创建,备份目标在各自的环境文件中指定。
services:
duplicati:
image: docker.1ms.run/duplicati/duplicati:2.4.1.0
ports:
- "127.0.0.1:8200:8200"
environment:
SETTINGS_ENCRYPTION_KEY: "${SETTINGS_ENCRYPTION_KEY:?set a key}"
DUPLICATI__WEBSERVICE_PASSWORD: "${DUPLICATI__WEBSERVICE_PASSWORD:?set a password}"
volumes:
- duplicati-data:/data
- ./source:/source:ro
- "${DUPLICATI_BACKUP_DIR:?set a backup directory}:/backups"
- ./restored:/restore
restart: unless-stopped
volumes:
duplicati-data:
docker compose -p "${project}-backup" --env-file .env.backup config -q &&
docker compose -p "${project}-backup" --env-file .env.backup pull &&
docker compose -p "${project}-backup" --env-file .env.backup up -d &&
docker compose -p "${project}-backup" --env-file .env.backup ps
等容器运行后,在主机浏览器打开 http://127.0.0.1:8200。如果主机没有桌面,从自己的电脑运行下列转发命令,将 user@host 替换为已能登录且已核对主机指纹的 SSH 账号和地址。保持会话打开,在电脑浏览器打开 http://127.0.0.1:18200。
ssh -N -o ExitOnForwardFailure=yes \
-L 127.0.0.1:18200:127.0.0.1:8200 user@host
拉取或启动失败时先按命令报错处理。若容器启动后退出,用原项目参数看日志,并在本机查看,避免把登录信息转发出去。
docker compose -p "${project}-backup" --env-file .env.backup logs --tail=40 duplicati
用 .env.backup 中的网页登录密码进入界面。点 Add backup → Add a new backup,建一个 deploy-notes-demo 任务。设好加密和独立的备份口令,来源选 /source,本地文件夹目标填 /backups,用 Test now 检查目标可访问。关闭自动调度,演练期间保留所有版本,提交。
首页点 Start。等结果显示成功,记下第一次备份时间,再到主机终端修改样例。
printf '%s\n' 'deploy notes B' > source/deploy-notes.txt
再次点 Start。第二次也要等到成功。若备份报错或演示文件被跳过,先核对任务的源目录、容器读权限和目标空间,两次均成功后再进行恢复演练。
切到空配置的新实例

确认没有备份任务正在运行。停止原项目后,将整个备份目录复制到最初建好的空目录 backup-copy。然后启动另一个项目。恢复实例的 /backups 会指向副本,原项目与原配置卷仍保存在主机上。
docker compose -p "${project}-backup" --env-file .env.backup stop &&
cp -a backup-store/. backup-copy/ &&
docker compose -p "${project}-recovery" --env-file .env.recovery config -q &&
docker compose -p "${project}-recovery" --env-file .env.recovery up -d &&
docker compose -p "${project}-recovery" --env-file .env.recovery ps
复制失败先处理访问权限或目标磁盘空间。用同一个浏览器地址重新打开,必要时退出旧会话,用 .env.recovery 的网页登录密码登录。首页应该没有 deploy-notes-demo。如果看到旧任务,先核对访问端口和当前 Compose 项目。若新容器启动失败,用 -p "${project}-recovery" --env-file .env.recovery 作为 Compose 参数查看该项目的日志。
进入 Restore,选 Direct restore from backup files。存储类型选择本地文件夹,填写 /backups,检查连接后继续,输入建任务时设置的备份加密口令。在版本列表里选第一次备份时间,勾选 deploy-notes.txt,把恢复位置改为新目录 /restore。选择保留现有文件的处理方式,如有恢复文件权限选项则关闭,提交并查看结果摘要。
验收恢复结果
在主机项目目录读两个文件。恢复出的路径可能包含子目录,用文件名找即可。
cat source/deploy-notes.txt
find restored -type f -name deploy-notes.txt -print -exec cat {
} \;
预期源文件显示 deploy notes B,恢复副本显示 deploy notes A。只看到最新版本时,先核对是否等到两次备份都成功,以及是否选中了第一次时间。版本列表为空时,检查 backup-copy 是否完整、网页路径是否为容器内的 /backups、备份口令是否与原任务一致。恢复已提交却没有文件,回到摘要核失败原因和 /restore 的写权限。
演练结束可以用原参数停止恢复项目,端口释放后再按原参数启动备份项目。
docker compose -p "${project}-recovery" --env-file .env.recovery stop
docker compose -p "${project}-backup" --env-file .env.backup up -d
此演练用于检查能否从备份文件恢复普通文件。正式迁移时,备份数据和口令需要各自妥善保管,尽量让备份所在介质独立于原主机。本文为参考配置,结果以实际备份和恢复摘要为准。