Vaultwarden 自建密码库:几百 MB 内存管住所有账号
同一套密码用了多少年?邮箱、论坛、几个购物站、家里的路由器后台,很多人翻出来会发现至少一半地方是同一个密码,最多在末尾换个数字。这种习惯的风险是乘法的:任何一个站点被拖库,攻击者拿着邮箱和密码去撞你另外几十个账号,撞中一个就再来一轮,然后越滚越大。改密码又疼,因为记不住。
正确的做法是每个站点一个随机密码,代价是你需要一个地方把这些密码存好。商业密码管理器按年订阅,用起来确实不错,只是把全部身家的钥匙交给一家公司这件事,很多人心里过不去。Vaultwarden 就是给这类人准备的。
它是什么,以及一条绕不过去的前提
一个极轻的服务端
它兼容主流密码管理器客户端的协议,也就是说你不需要换习惯:浏览器扩展、桌面程序、手机 App 都照常用,只是登录时把服务器地址填成你自己的。服务端本身的资源占用低到不像话,几百 MB 内存就能跑,一台最便宜的小机器、一个树莓派、一个 NAS 上的容器都够。
| 项目 | 内容 |
|---|---|
| 费用 | 商业方案按年按人订阅;自建只有服务器成本,接近零 |
| 数据位置 | 商业方案在厂商机房;自建在你自己的硬盘上 |
| 功能完整度 | 商业方案更全面;它的核心功能齐全,多用户共享和附件够用 |
| 加密方式 | 两者都是客户端加密,服务端只存密文 |
| 主要风险 | 商业方案是厂商被黑或停止服务;自建是你自己操作失误 |
| 谁在维护 | 商业方案是厂商;自建是你,包括证书、升级、备份 |
有一点必须理解清楚:密码库是在客户端加密之后才上传的,服务端手里是密文。所以理论上连你自己搭的服务端也看不到明文。这既是好事,也意味着主密码丢了谁都没法救你,没有任何「找回密码」按钮。
为什么必须 HTTPS
这不是建议,是硬性要求。客户端在握手阶段就会检查服务器是不是走的安全协议,明文地址直接拒绝连接,最多允许在本机测试时凑合。原因很实在:解锁和保存的过程中,客户端要和服务器交换密钥材料,走明文等于把密码库裸奔在网络上。
所以完整顺序是:先有域名,先有证书,再谈客户端接入。反过来做,你会在一堆「连接失败」里浪费一晚上。
Docker 部署
mkdir -p /opt/vaultwarden/data
docker run -d \
--name vaultwarden \
--restart unless-stopped \
-p 127.0.0.1:8080:80 \
-v /opt/vaultwarden/data:/data \
-e TZ=Asia/Shanghai \
-e DOMAIN=你的对外域名 \
-e ADMIN_TOKEN=换成一串随机长字符串 \
-e SIGNUPS_ALLOWED=true \
-e WEBSOCKET_ENABLED=true \
vaultwarden/server
几个要点逐个说:
- 端口绑在 127.0.0.1 而不是 0.0.0.0,让它只能被本机的反向代理访问,不直接暴露在公网
- 数据目录是全部身家:数据库、附件、图标缓存、密钥文件全在里面
- 管理令牌务必用一串随机字符串,不要用能猜到的词,它等于后台的最高权限
- 注册开关第一次部署先放开,建完自己的账号马上关回去
- 对外域名会影响邀请链接和一些反馈地址的生成,按最终地址填
compose 写法
services:
vaultwarden:
image: vaultwarden/server
container_name: vaultwarden
restart: unless-stopped
ports:
- "127.0.0.1:8080:80"
volumes:
- ./data:/data
environment:
TZ: Asia/Shanghai
DOMAIN: 你的对外域名
ADMIN_TOKEN: 换成一串随机长字符串
SIGNUPS_ALLOWED: "false"
INVITATIONS_ALLOWED: "true"
WEBSOCKET_ENABLED: "true"
SHOW_PASSWORD_HINT: "false"
邀请开关打开后,公开注册关着也不影响你用后台给家人发邀请,这是个很舒服的组合:外人进不来,家人能自助注册。
Nginx 反代和证书
server {
listen 443 ssl;
server_name vault.example.com;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
client_max_body_size 128m;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
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;
}
location /notifications/hub {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}
三个地方最容易出问题:请求体上限太小会让附件上传失败;X-Forwarded-Proto 忘了设,服务端以为你在走明文,客户端就会提示服务器不安全甚至拒绝连接;websocket 那段不配,多设备之间的实时推送就没了,你在一台上改的密码要等另一台刷新才出现。
证书用自动续期的客户端搞定,续期后记得让 Nginx reload。配完就不管,是半年后突然全员登录不了的经典原因。
客户端怎么连上你自己的服务器
浏览器扩展
装好扩展,在登录界面上找「自托管」入口或者设置里的服务器地址一栏:协议选安全的那一项,主机名填你反代出来的域名,不要带多余路径。保存后再登录,界面会变成你自建服务器的登录页。
手机 App
登录页通常有一个齿轮或者区域设置的图标,点开能填服务器地址。同样填域名,成功后登录页上会显示你的服务器标识。填错了的表现是一直转圈或者报网络错误。
最常见的三种填错
- 协议写成明文的那一项,客户端直接拒绝
- 把端口号也写上了。反代之后对外就是标准的安全端口,不需要额外写
- 把后台路径拼进去了,客户端去请求一个不存在的接口
管理后台和注册开关
访问 /admin 路径,输入管理令牌就进后台。能做的是:看用户列表、邀请新用户、删除用户、查看诊断信息、触发一次备份、改少量全局设置。日常用得最多的就是邀请家人。
关闭公开注册的正确顺序:先自己注册好并登录,再确认能在浏览器扩展和手机上正常同步,最后才把注册开关改成关闭并重启容器。反过来做,你会发现自己也被锁在外面,只能去数据库里手改,或者临时把开关再打开一次。
还有一句提醒:管理令牌泄露等于别人能重置用户、能改配置,别把它和你的登录密码混在一起,也别在聊天窗口里贴出来。
备份与恢复
要备份的就是数据目录。db.sqlite3 是主数据库,attachments 是附件,sends 是一次性分享的内容,另外还有图标缓存和密钥文件。少哪一块都可能出问题,整目录打包最省心。
docker stop vaultwarden
tar -czf /backup/vaultwarden-$(date +%F).tar.gz -C /opt/vaultwarden data
docker start vaultwarden
停容器再打包是最稳的方式,数据库在写入过程中被直接拷贝有可能拿到不一致的文件。备份完把包挪到另一台机器或者另一块盘上——和原数据放在同一块硬盘里的备份,等于没有备份。
恢复就是把包解开、目录放回原位、重新起容器。换机器时注意目录属主,容器里的运行用户要能读写这些文件,否则启动就报权限错误。
几个真会踩到的坑
- 没 HTTPS,客户端直接拒绝登录。别试图绕过,这是设计而不是配置问题。
- 改了域名之后所有客户端报错。客户端里存的是旧地址,需要在设置里改过来,多数情况下还要重新登录一次。
- 注册关早了把自己锁在外面。按上面的顺序来,先注册后关闭。
- 两步验证的恢复码没存。手机丢了、验证器被清了,没有恢复码的话恢复流程非常麻烦。开启时就把恢复码打出来收好,别只截一张存在手机相册里。
- 反向代理没设转发协议头。表现是客户端提示连接不安全,或者后台里看到一堆来源异常的会话。
- 只备份数据库不备份附件。附件是独立目录,漏了它,恢复之后所有文件都变成「附件不存在」。
适合你,不适合你
适合:愿意为几十个账号换上随机密码;手里有域名和一台常开的机器;能接受「备份这件事归我管」。
不适合:完全不想碰命令行和证书;家里没有人能接手这套东西——真出事的时候,家人连数据在哪、怎么恢复都不知道。这种情形下,付钱给商业服务反而更负责任。






