自建密码管理器:Vaultwarden 部署与使用指南
为什么要自建
现在多数人用浏览器自带的密码管理器,或者 1Password、LastPass 这样的商业服务。这些方案各有短板:
- 浏览器管理器:跨浏览器不互通,换设备就断档。
- 商业服务:密码存在别人的服务器上,LastPass 历史上出过多次数据泄露事件。
- 同步焦虑:手机、平板、笔记本和台式机之间,密码不一致是最常见的痛点。
自建方案可以把数据留在自己的 NAS 上,同时享受全平台自动填充。一次配置,长期受益。
Vaultwarden 是什么
Vaultwarden 是 Bitwarden 服务端的 Rust 重写版本。与官方的 Bitwarden Server(需要 .NET + SQL Server,内存起步 2GB)不同,Vaultwarden 用 SQLite 做存储,空闲时内存占用不到 20MB,在树莓派上都能跑。
它完全兼容 Bitwarden 的官方客户端——浏览器插件、桌面端、手机 App 全部通用。也就是说,你部署的是一个轻量的 Vaultwarden 服务端,但所有用户使用的都是 Bitwarden 原厂客户端。
| 对比 | 官方 Bitwarden | Vaultwarden |
|---|---|---|
| 运行时 | .NET + SQL Server | Rust + SQLite |
| 内存占用 | ~2 GB | ~20 MB |
| 高级功能(TOTP/组织) | 付费解锁 | 全部免费 |
| 硬件要求 | 中高配 VPS | 树莓派即可 |
环境准备
需要一台 24 小时开机的 Linux 主机或 NAS。下面以群晖 DSM + Container Manager 为例,其他平台只需调整数据卷路径。
前置要求:
- Docker 和 Docker Compose 已安装(群晖 DSM 7.2 自带 Container Manager)
- 一个域名(本文以
vault.example.com为例) - 域名的 DNS 已解析到 NAS 的 IP
群晖上创建好目录结构:
mkdir -p /volume1/docker/vaultwarden/data
data/ 目录下会存放 SQLite 数据库和附件,这是最重要的目录——备份就备份它。
Docker Compose 部署
在 /volume1/docker/vaultwarden/ 下创建 docker-compose.yml:
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
volumes:
- /volume1/docker/vaultwarden/data:/data
ports:
- "127.0.0.1:8088:80"
environment:
- DOMAIN=https://vault.example.com
- SIGNUPS_ALLOWED=false
- ADMIN_TOKEN=${ADMIN_TOKEN}
- WEBSOCKET_ENABLED=true
- LOG_FILE=/data/vaultwarden.log
- LOG_LEVEL=warn
- TZ=Asia/Shanghai
关键参数解释:
127.0.0.1:8088:80:仅监听本地回环,外部流量必须走 Nginx 反向代理,杜绝直连风险。SIGNUPS_ALLOWED=false:部署完成后关闭注册。首次使用时先设为true,注册好账号后再改回false。ADMIN_TOKEN:管理面板密码的 argon2id 哈希。用下面命令生成:
# 生成 ADMIN_TOKEN(替换 your-secret-password)
docker run --rm vaultwarden/server /vaultwarden hash --preset owasp
把输出的哈希值写入 .env 文件:
echo 'ADMIN_TOKEN=$argon2id$v=19$m=65540,t=3,p=4$...' > /volume1/docker/vaultwarden/.env
然后启动:
cd /volume1/docker/vaultwarden
docker compose up -d
访问管理面板:https://vault.example.com/admin,用刚才生成的 ADMIN_TOKEN 登录。
Nginx 反向代理配置
Vaultwarden 需要 WebSocket 支持才能实现实时同步,Nginx 配置需要特别注意。
在群晖 Container Manager 中为 Nginx 创建容器(或复用已有),挂载以下配置:
server {
listen 443 ssl http2;
server_name vault.example.com;
# SSL 证书(下节详述)
ssl_certificate /etc/nginx/certs/vault.example.com/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/vault.example.com/privkey.pem;
client_max_body_size 128M;
location / {
proxy_pass http://172.17.0.1:8088;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# WebSocket 支持(通知实时推送的关键)
location /notifications/hub {
proxy_pass http://172.17.0.1:8088;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location /notifications/hub/negotiate {
proxy_pass http://172.17.0.1:8088;
}
}
# HTTP → HTTPS 强制跳转
server {
listen 80;
server_name vault.example.com;
return 301 https://$server_name$request_uri;
}
172.17.0.1是 Docker 默认 bridge 网络的网关地址,容器通过它访问宿主机端口。如果你的 Docker 网络不同,用ip addr show docker0确认。
HTTPS 证书配置
既然是密码管理器,HTTPS 是硬性要求——客户端拒绝通过 HTTP 连接。
推荐使用 acme.sh 申请 Let’s Encrypt 免费证书,支持 DNS API 自动续期。以阿里云 DNS 为例:
# 安装 acme.sh
curl https://get.acme.sh | sh
# 设置阿里云 API 密钥
export Ali_Key="LTAI5t..."
export Ali_Secret="..."
# 申请泛域名证书
acme.sh --issue --dns dns_ali -d example.com -d '*.example.com'
# 安装证书到 Nginx 目录
acme.sh --install-cert -d example.com \
--key-file /volume1/docker/nginx/certs/vault.example.com/privkey.pem \
--fullchain-file /volume1/docker/nginx/certs/vault.example.com/fullchain.pem \
--reloadcmd "docker exec nginx nginx -s reload"
acme.sh 会自动添加 cron 任务,证书过期前自动续期。
如果域名托管在其他 DNS 服务商,acme.sh 支持 超过 150 种 DNS API,包括 Cloudflare、DNSPod、Namecheap 等。
客户端配置
部署完成并配置好 HTTPS 后,下载 Bitwarden 官方客户端:
| 平台 | 下载 |
|---|---|
| 浏览器插件 | Chrome / Firefox / Edge |
| 桌面端 | Windows / macOS / Linux |
| 移动端 | iOS App Store / Android Google Play |
每个客户端首次打开时,点击左上角齿轮图标,在"自托管"中填入你的服务器地址:
https://vault.example.com
之后用你注册的邮箱和主密码登录即可。所有密码数据端到端加密,服务端只能看到密文。
实用技巧
开启 TOTP 两步验证:Vaultwarden 内置 TOTP 生成器。给每个网站添加 TOTP 密钥后,登录时自动填充验证码,不用再切换到 Google Authenticator。
密码库自动锁定:桌面端设置 15 分钟自动锁定,浏览器插件设置"浏览器重启时锁定"。避免离开电脑后密码库敞开。
紧急访问:在组织设置中指定受信任的联系人,对方可以在你指定的等待期(如 7 天)后获得只读访问权限——真正的"数字遗产"方案。
备份策略
密码库的价值极高,备份需要严格对待:
#!/bin/bash
# /volume1/scripts/backup-vaultwarden.sh
BACKUP_DIR="/volume1/backup/vaultwarden"
DATA_DIR="/volume1/docker/vaultwarden/data"
TIMESTAMP=$(date +%Y%m%d_%H%M)
RETENTION_DAYS=30
# 停止容器保证数据一致性
docker stop vaultwarden
# 打包数据目录
tar -czf "$BACKUP_DIR/vaultwarden_$TIMESTAMP.tar.gz" -C "$DATA_DIR" .
# 重启容器
docker start vaultwarden
# 删除 30 天前的旧备份
find "$BACKUP_DIR" -name "vaultwarden_*.tar.gz" -mtime +$RETENTION_DAYS -delete
echo "Backup done: vaultwarden_$TIMESTAMP.tar.gz"
加入 crontab 每日凌晨执行:
0 3 * * * /bin/bash /volume1/scripts/backup-vaultwarden.sh >> /volume1/scripts/backup.log 2>&1
备份 3-2-1 原则:至少 3 份副本,存储在 2 种不同介质上,其中 1 份在异地。除了 NAS 本地备份,建议用 Syncthing 或 rclone 再同步一份到另一台设备或云存储。
安全加固建议
部署密码管理器,安全方面不能马虎:
- 关闭注册后务必设置
SIGNUPS_ALLOWED=false,从源头杜绝未授权访问。 - 定期更新镜像:
docker compose pull && docker compose up -d,Vaultwarden 的更新节奏大约是每月一次。 - 数据库加密:如果你的 NAS 支持文件系统加密(群晖有 Shared Folder Encryption),把
/volume1/docker/vaultwarden/data放在加密文件夹里。 - 主密码要强:12 位以上,包含大小写字母、数字和特殊字符。这串密码记在脑子里,不要存在任何地方。
- 设置失败锁定:管理面板中可配置"多次登录失败后临时封禁 IP"。
- 查看审计日志:管理面板的 “Diagnostics” 页面能查看所有登录记录,定期检查是否有机器的异常尝试。
- 关闭不必要的端口映射:不要暴露 Vaultwarden 的直接端口(8088),只有 Nginx 的 443 对外开放。
常见问题排查
客户端提示"发生错误,无法连接服务器"
检查三个地方:
- 域名是否能正常解析:
nslookup vault.example.com - Nginx 是否在运行:
docker ps | grep nginx - HTTPS 证书是否过期:浏览器打开
https://vault.example.com看证书详情
WebSocket 连接失败(无法实时同步)
浏览器插件 F12 看 Console,如果有 WebSocket 报错,确认 Nginx 配置中的 /notifications/hub 和 /notifications/hub/negotiate 两个 location 块是否正确。
忘记主密码怎么办
无法找回。密码库是端到端加密的,主密码丢失就意味着数据永久不可恢复。这也是为什么备份里要包含一份用不同方式加密的导出:
定期在桌面端执行:文件 → 导出密码库 → .json(加密),用另一个独立的密码加密后,存到安全的地方。
更新后启动失败
看日志:docker logs vaultwarden。最常见的原因是 SQLite 数据库版本不兼容,Vaultwarden 通常会自动迁移,但如果从很老的版本跳升,可能需要手动操作。建议至少每季度更新一次,每次只跨一个小版本。
自建密码管理器是"数据主权"最直接的体现之一。从注册到全平台同步,整个流程大约半小时,却能用很多年。如果这篇文章对你有帮助,欢迎在评论区交流你的部署经验或踩过的坑。
评论功能基于 Giscus(GitHub Discussions)——在 repo 启用 Discussions 后,到 giscus.app 获取仓库 ID 填入
src/_data/site.json即可启用。