搭建 Vaultwarden 个人密码保险箱
摘要:本文介绍在PVE家庭云服务中部署自托管密码管理器Vaultwarden,搭配Bitwarden客户端实现端到端加密。通过Docker Compose与Caddy配置HTTPS,完成部署。备份方面,编写脚本停止服务压缩数据,保留30天,并上传至阿里云OSS实现容灾,最后通过crontab定时每天凌晨4点自动备份。
前言
书接上回《基于 PVE 搭建 All In One 家庭云服务》,在家的小主机上搭建了一套虚拟化环境并安装了 Docker,就需要安装自托管应用让它发挥价值了。
就我个人而言,我的大部分密码都存在了支付宝一个叫“钢铁匣”的小程序里,2FA 则使用的是谷歌的身份验证器 APP。每次打开网站需要登录输入账密时,都要打开小程序找半天密码,很是费事。而且大部分密码虽然都长差不多,但都不相同,记忆不了一点。
为了摆脱记忆的困扰,以及密码的安全(保证每个平台密码不一样),就需要使用到密码管理器了。我选用的是 Vaultwarden1 作为服务端,搭配 Bitwarden2 的客户端,做到密码的端到端加密且自托管。
至于为什么不直接使用 Bitwarden 呢?一方面是 Bitwarden 的免费计划不支持 2FA 密钥,另一方面是听说它的私有部署比较重量级,而 Vaultwarden 比较轻量化且完全兼容 Bitwarden 客户端。
本文一方面介绍部署和使用,但是最重要的还是需要保证数据的可靠性,通过备份以及上传异地,保证密码数据不丢。
部署
通过 Docker Compose 编排部署,因为 Vaultwarden 要求访问必须使用 HTTPS,所以需要加上一个 Caddy 反向代理自动颁发证书。
docker-compose.yml编排文件如下:
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
volumes:
- ./vw-data:/data # 数据持久化目录
networks:
- vault-net
environment:
# 必须替换为你的真实域名(需包含 https://)
- DOMAIN=https://vw.local
# 时区
- TZ=Asia/Shanghai
# 首次部署设为 true 以注册管理员,注册后务必改为 false
- SIGNUPS_ALLOWED=true
# 生成管理员令牌,用于访问后台 /admin 管理面板。首次设置完成后置空,有需要再临时填入
- ADMIN_TOKEN=123123
# 是否启用网页端
- WEB_VAULT_ENABLED=true
# 是否启用多端实时同步
- WEBSOCKET_ENABLED=true
# 是否开始加密分享
- SENDS_ALLOWED=false
caddy:
image: caddy:2
container_name: caddy
restart: always
ports:
# HTTP 端口,用于 ACME 验证和自动跳转 HTTPS
- "80:80"
# HTTPS 端口
- "443:443"
volumes:
- ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro
- ./caddy/data:/data
- ./caddy/config:/config
- ./caddy/logs:/logs
environment:
# 同意 Let's Encrypt 服务条款
- ACME_AGREE=true
networks:
- vault-net
depends_on:
- vaultwarden
networks:
vault-net:
driver: bridge 配置caddy/Caddyfile对 Vaultwarden 进行反向代理:
vw.local {
tls internal # 只在内网使用时,使用自签名证书。外网使用去掉这行
reverse_proxy vaultwarden:80
}
启动项目:
docker compose up -d然后在本地配置 host 文件指定vw.local的 IP 为 Docker 的 IP。浏览器访问时,会提示证书不可信,要么手动信任,要么手动安装 caddy 的根证书到设备上。
之后创建账号,登录使用即可。创建账号后,务必关闭注册,然后重新启动项目。
备份
在 Vaultwarden Wiki 中有提到相关备份事项3,因为是个人使用,我采用的是非使用期间停机完整备份,保证数据一点不丢。
编写备份脚本backup.sh,包含停机、备份、压缩、重启、清理历史:
#!/bin/bash
set -e
# 1. 目录切换与检查
cd "$(dirname "$0")"
[ -f "docker-compose.yml" ] || { echo "❌ 找不到 compose 文件"; exit 1; }
[ -d "./vw-data" ] || { echo "❌ 数据目录不存在"; exit 1; }
# 2. 停止服务
echo "🛑 停止 Vaultwarden..."
docker compose down
sleep 10
# 3. 备份
echo "📦 压缩备份..."
datestamp=$(date +%Y%m%d_%H%M%S)
backup_dir="./vw-backups"
mkdir -p "${backup_dir}"
if zip -9 -r "${backup_dir}/${datestamp}.zip" ./vw-data/ && [ -f "${backup_dir}/${datestamp}.zip" ]; then
file_size=$(du -h "${backup_dir}/${datestamp}.zip" | cut -f1)
echo "✅ 备份文件创建成功: $file_size"
else
echo "❌ 备份创建失败"
exit 1
fi
# 4. 启动服务
echo "🚀 启动 Vaultwarden..."
docker compose up -d
sleep 10
SERVICE_NAME=$(docker compose config --services | head -1)
if docker compose ps "$SERVICE_NAME" | grep -q "Up"; then
echo "✅ $SERVICE_NAME 已启动"
else
echo "❌ $SERVICE_NAME 启动失败"
docker compose ps
exit 1
fi
# 5. 清理
echo "🧹 清理30天前的备份..."
deleted=$(find "$backup_dir" -name "*.zip" -type f -mtime +30 -delete -print | wc -l)
remaining=$(find "$backup_dir" -name "*.zip" -type f | wc -l)
echo "📊 删除了 $deleted 个旧备份,当前保留 $remaining 个备份"
echo "✅ 备份完成!"先安装脚本中使用到的压缩工具,添加运行权限,即可运行:
apt install zip
chmod +x backup.sh
./backup.sh而 Vaultwarden 的所有数据都挂载到宿主机的vw-data数据目录,所以备份恢复只需要停机、删除vw-data目录、解压备份文件转移到数据目录即可。
容灾
当备份文件与项目都运行在同一台机器时,对重要数据来说是很不安全的。万一机器崩溃了,备份就丢失了,不妥。
这里要做第二份备份,把备份文件上传到对象存储中。这里使用阿里云 OSS 对象存储提供的命令行工具完成。
使用前,安装并配置ossutil4,写入配置文件ossutil.ini,并按照实际修改:
[default]
accessKeyID=yourAccessKeyID
accessKeySecret=yourAccessKeySecret
# region 需要修改成实际存储桶所在地域
region=cn-shenzhen
buckets=prod-bucket
[buckets prod-bucket]
# 把 zyaer-vw-backup 修改成实际存储桶名
zyaer-vw-backup =
# 同地域ECS可以设置成内网endpoint,也可以设置成不同地域的外网endpoint,理论上不同地域更安全
endpoint=https://oss-cn-shenzhen.aliyuncs.com
然后在备份脚本尾部添加阿里云上传脚本代码:
# 6. 备份容灾
echo "☁️ 正在上传最新备份至阿里云OSS..."
BACKUP_FILE_PATH="./vw-backups/${datestamp}.zip"
# 需要注意,这里 zyaer-vw-backup 需要修改成自己实际存储桶名
OSS_TARGET_PATH="oss://zyaer-vw-backup/"
if ossutil -c ./ossutil.ini cp "$BACKUP_FILE_PATH" "$OSS_TARGET_PATH" -f; then
echo "✅ 备份文件已成功上传至 $OSS_TARGET_PATH"
else
echo "❌ 备份上传失败,请检查配置。"
# 根据你的需求决定是否让脚本在此失败
# exit 1
fi备份上传成功示例如下:
定时
直接使用 Linux 系统的 crontab,首次运行会提示选择文本编辑器:
crontab -e在打开的文件末尾新增定时器,每天凌晨4点执行备份脚本,保存并退出文本即可。
# 脚本中路径修改成实际绝对路径
0 4 * * * /root/vaultwarden/backup.sh >> /root/vaultwarden/backup.log 2>&1结语
最后,我们的项目目录如下:
./vaultwarden/
├── docker-compose.yml # 容器编排文件
├── caddy/ # Caddy 数据目录
│ └── Caddyfile # 反向代理配置文件
├── vw-data/ # Vaultwarden 数据目录
├── vw-backups/ # Vaultwarden 备份目录(自动生成)
├── ossutil.ini # 阿里云 OSS 配置文件
├── backup.log # 备份脚本运行日志(自动生成)
└── backup.sh # 备份脚本
至此,我们就拥有了一个只属于自己的个人密码保险箱,所有数据都是加密的,只要主密码不泄露,我们的所有账号密码都不会泄露。同时备份与容灾也给了我们的密码一个保障,避免单点故障导致的数据丢失。
按照本文搭建好服务端后,如果是内网设备则再接入内网穿透,后续配合 Bitwarden 的各种客户端,即可享受到多端同步的乐趣了。
Footnotes
搭建 Vaultwarden 个人密码保险箱
https://www.zyaer.com/posts/set-up-vaultwarden-personal-password-safe