用 rclone 加密备份 Docker Vaultwarden 到 OneDrive
在 Debian VPS 上为 Docker Vaultwarden 建立自动备份:生成一致性 SQLite 快照、打包关键数据、使用 rclone crypt 客户端加密并上传到 OneDrive。
Vaultwarden 保存的是密码、TOTP 秘钥、附件和服务配置,不能只依赖 VPS 磁盘或单一 Docker 卷。本文记录如何在 Debian VPS 上建立一条自动化备份链路:
Vaultwarden Docker 容器→ 内置 backup 生成一致性 SQLite 快照→ 打包数据库、RSA 密钥及可选附件→ rclone crypt 在 VPS 本地加密→ 直接上传到 OneDrive→ cron 每天自动执行本文的关键原则:
- 不直接复制正在运行的
db.sqlite3 - 不把密码库归档以明文上传到 OneDrive
- 不使用
rclone sync做备份上传,避免误删远端历史 - 上传成功后才清理过期的本地文件
- 配置完成后,Windows 不参与日常备份;数据由 VPS 直接上传到 OneDrive
1. 确认 Vaultwarden 数据目录
如果 Vaultwarden 通过 Docker 运行,先从容器实际挂载中确认宿主机数据目录,不要凭印象猜路径:
docker inspect vaultwarden \ --format '{{range .Mounts}}{{println .Source "->" .Destination}}{{end}}'示例:
/opt/apps/vaultwarden/data -> /data查看数据目录:
ls -lah /opt/apps/vaultwarden/data常见关键内容包括:
db.sqlite3db.sqlite3-waldb.sqlite3-shmrsa_key.pemattachments/sends/config.json实际存在什么就备份什么。attachments/、sends/ 和 config.json 在不同部署中可能不存在。
2. 使用 Vaultwarden 内置命令生成数据库快照
运行中的 SQLite 数据库如果处于 WAL 模式,会存在:
db.sqlite3-waldb.sqlite3-shm因此不应把在线使用中的 db.sqlite3 当作唯一备份来源。Vaultwarden 内置命令会创建一致性的数据库快照:
docker exec vaultwarden /vaultwarden backup成功时会得到类似输出:
Backup to 'data/db_YYYYMMDD_HHMMSS.sqlite3' was successful快照会出现在 Vaultwarden 的宿主机数据目录中,例如:
/opt/apps/vaultwarden/data/db_YYYYMMDD_HHMMSS.sqlite3查找最新快照:
LATEST_DB="$( find /opt/apps/vaultwarden/data \ -maxdepth 1 \ -type f \ -name 'db_*.sqlite3' \ -printf '%T@ %p\n' | sort -nr | head -n 1 | cut -d' ' -f2-)"
printf '%s\n' "$LATEST_DB"3. 配置 rclone 与 OneDrive
安装较新的 rclone 版本后,先配置普通 OneDrive remote:
rclone config建议建立:
onedrive:对于无图形界面的 VPS,OneDrive 的首次 OAuth 授权需要借用一台有浏览器的设备完成一次登录;授权 token 最终保存在 VPS 的 rclone 配置中。之后的上传和定时备份均由 VPS 独立执行,不需要 Windows 开机或参与文件传输。
验证 OneDrive remote:
rclone listremotesrclone lsd onedrive:4. 建立 rclone crypt 加密层
普通 onedrive: 不应直接作为 Vaultwarden 备份目标。应在它上面创建一个 crypt remote,例如:
vaultcrypt:其底层路径设为:
onedrive:EncryptedBackups后续脚本只使用:
vaultcrypt:Vaultwarden不要直接使用:
onedrive:EncryptedBackupsrclone 会在 VPS 本地完成文件内容加密,默认还会加密文件名和子目录名。OneDrive 网页中看到乱码文件名属于正常现象;通过 vaultcrypt: 才能看到解密后的真实名称。
测试链路:
printf 'rclone crypt upload test %s\n' "$(date -u +'%FT%TZ')" \ > /tmp/rclone-upload-test.txt
rclone copyto --progress \ /tmp/rclone-upload-test.txt \ vaultcrypt:ConnectionTest/rclone-upload-test.txt
rclone lsl vaultcrypt:ConnectionTest
rm -f /tmp/rclone-upload-test.txt5. 自动备份脚本
创建脚本目录、备份目录和日志目录:
install -d -m 700 /opt/scriptsinstall -d -m 700 /opt/backups/vaultwardeninstall -d -m 700 /var/log/vaultwarden-backup创建 /opt/scripts/backup-vaultwarden.sh:
#!/usr/bin/env bashset -Eeuo pipefailumask 077
CONTAINER="vaultwarden"VW_DATA="/opt/apps/vaultwarden/data"LOCAL_DIR="/opt/backups/vaultwarden"REMOTE="vaultcrypt:Vaultwarden"KEEP_DAYS=14
STAMP="$(date -u +'%Y-%m-%dT%H-%M-%SZ')"STAGE="${LOCAL_DIR}/stage-${STAMP}"ARCHIVE="${LOCAL_DIR}/vaultwarden-${STAMP}.tar.gz"
log() { printf '%s %s\n' "$(date -u +'%FT%TZ')" "$*"}
cleanup_stage() { rm -rf "$STAGE"}trap cleanup_stage EXIT
require_file() { if [ ! -f "$1" ]; then log "ERROR: required file is missing: $1" exit 1 fi}
log "Starting Vaultwarden backup"
require_file "$VW_DATA/rsa_key.pem"
mkdir -p "$STAGE"
docker exec "$CONTAINER" /vaultwarden backup
LATEST_DB="$( find "$VW_DATA" -maxdepth 1 -type f -name 'db_*.sqlite3' -printf '%T@ %p\n' | sort -nr | head -n 1 | cut -d' ' -f2-)"
if [ -z "${LATEST_DB:-}" ] || [ ! -f "$LATEST_DB" ]; then log "ERROR: Vaultwarden did not create a database snapshot" exit 1fi
cp -a "$LATEST_DB" "$STAGE/db.sqlite3"cp -a "$VW_DATA"/rsa_key* "$STAGE/"
if [ -d "$VW_DATA/attachments" ]; then cp -a "$VW_DATA/attachments" "$STAGE/"fi
if [ -d "$VW_DATA/sends" ]; then cp -a "$VW_DATA/sends" "$STAGE/"fi
if [ -f "$VW_DATA/config.json" ]; then cp -a "$VW_DATA/config.json" "$STAGE/"fi
tar -C "$LOCAL_DIR" -czf "$ARCHIVE" "$(basename "$STAGE")"
if ! tar -tzf "$ARCHIVE" >/dev/null; then log "ERROR: archive integrity check failed: $ARCHIVE" exit 1fi
log "Uploading $(basename "$ARCHIVE")"rclone copyto --retries 3 --low-level-retries 10 \ "$ARCHIVE" \ "$REMOTE/$(basename "$ARCHIVE")"
if ! rclone lsf "$REMOTE" | grep -Fqx "$(basename "$ARCHIVE")"; then log "ERROR: uploaded archive was not found through crypt remote" exit 1fi
# 删除远端超过 KEEP_DAYS 天的 vaultwarden-*.tar.gzrclone delete "$REMOTE" \ --include 'vaultwarden-*.tar.gz' \ --min-age "${KEEP_DAYS}d"
find "$LOCAL_DIR" -maxdepth 1 -type f -name 'vaultwarden-*.tar.gz' \ -mtime +"$KEEP_DAYS" -print -delete
find "$VW_DATA" -maxdepth 1 -type f -name 'db_*.sqlite3' \ -mtime +"$KEEP_DAYS" -print -delete
log "SUCCESS: $(basename "$ARCHIVE")"赋予权限:
chmod 700 /opt/scripts/backup-vaultwarden.sh先检查语法:
bash -n /opt/scripts/backup-vaultwarden.sh再手动运行一次并查看日志:
/opt/scripts/backup-vaultwarden.sh 2>&1 | tee -a /var/log/vaultwarden-backup/backup.log成功时应出现:
Starting Vaultwarden backupUploading vaultwarden-...SUCCESS: vaultwarden-...验证远端备份:
rclone lsl vaultcrypt:Vaultwarden6. 设置 cron 自动运行
确认手动执行成功后,编辑 root 的 crontab:
crontab -e每天凌晨 03:20 执行:
20 3 * * * /opt/scripts/backup-vaultwarden.sh >> /var/log/vaultwarden-backup/backup.log 2>&1确认已生效:
crontab -l查看日志:
tail -n 50 /var/log/vaultwarden-backup/backup.log检查 cron 服务:
systemctl status cron --no-pager需要注意:cron 使用哪个用户运行,就要能读取该用户的 rclone 配置。本文以 root 配置 rclone,并使用 root 的 crontab,因此配置文件位于:
/root/.config/rclone/rclone.conf7. 恢复验证
上传成功不等于备份一定可恢复。恢复测试应从加密 remote 下载,而不是直接从 OneDrive 网页下载密文文件。
先列出远端归档:
rclone lsl vaultcrypt:Vaultwarden下载指定备份到隔离目录:
install -d -m 700 /opt/restore/vaultwarden
rclone copyto \ vaultcrypt:Vaultwarden/vaultwarden-YYYY-MM-DDTHH-MM-SSZ.tar.gz \ /opt/restore/vaultwarden/vaultwarden-YYYY-MM-DDTHH-MM-SSZ.tar.gz检查归档是否可读取:
tar -tzf /opt/restore/vaultwarden/vaultwarden-YYYY-MM-DDTHH-MM-SSZ.tar.gz至少应看到:
.../db.sqlite3.../rsa_key.pem如果 Vaultwarden 使用过附件,还应看到:
.../attachments/不要仅因为 OneDrive 上显示文件存在就删除本地副本。应定期进行独立的恢复演练。
8. 必须保护的恢复材料
完成自动化后,还必须保护以下内容:
- OneDrive 账号访问权
vaultcrypt:的加密密码vaultcrypt:的 salt/root/.config/rclone/rclone.conf- Vaultwarden 的 Docker Compose / Dockge stack 配置
.env中的部署配置与密钥
rclone 配置文件含有 OneDrive token 和 crypt 参数,应限制权限:
chmod 600 /root/.config/rclone/rclone.conf不要把 crypt 密码与 salt 只存放在 Vaultwarden 中。因为 Vaultwarden 恰好是正在备份的对象,恢复材料还应在独立、安全的位置留存一份。
总结
完成后,日常流程不再需要人工下载、压缩或上传:
cron→ Vaultwarden backup→ tar.gz→ rclone crypt→ OneDrive关键点不是“文件上传成功”,而是:
- 数据库快照来自 Vaultwarden 内置 backup。
- 数据离开 VPS 前已完成客户端加密。
- 数据库、RSA 密钥和未来的附件会一并保存。
- 不使用危险的
rclone sync覆盖远端历史。 - 定期验证可以下载、解密和读取归档。