外观
Vaultwarden:自托管密码管理
完成后,你有一个兼容 Bitwarden 客户端的自托管密码库:浏览器扩展、手机 App 都能用,走 HTTPS,关闭了公开注册,管理页令牌是哈希过的,并且有服务器端备份和离线备份两层保护。
Vaultwarden 是第三方实现,和 Bitwarden 公司没有关系。
适合谁
- 想自己保管密码,并且愿意为它负责:备份、更新、证书,一样都不能马虎。
- 不适合:没有备份习惯,或者家里人用但你处理不了故障。这种情况,直接用官方的 Bitwarden 托管服务更稳妥。
密码库是你最敏感的数据,先读这一段
- 影响范围:密码库泄漏,等于你所有账号泄漏;服务器上的数据丢了又没有备份,你就失去所有密码。它还是一个值得被盯上的目标。
- 前置条件:必须有 HTTPS(下一节解释为什么)。先把备份做好、演练过,再往里存密码。不要直接暴露到公网:外网访问优先用 VPN,见远程访问:别把 NAS 直接暴露到公网;非要对外,只通过带 HTTPS 的反向代理。
- 怎么回退:关掉路由器上的端口转发,停止容器。密码库里的内容不会丢,数据仍在数据目录里。另外保留一份离线导出,见下。
应用信息
| 项 | 内容 |
|---|---|
| 示例清单 | 有 vaultwarden,示例文件日期 2025-06-01 |
| 镜像 | vaultwarden/server:latest,也可以用 ghcr.io/dani-garcia/vaultwarden |
| 示例里的端口 | 80,会和 OMV 的 Web 界面撞车 |
| 数据 | /data:SQLite 数据库 db.sqlite3、附件、配置、密钥文件 |
| 官方文档 | Vaultwarden wiki |
为什么必须 HTTPS
Vaultwarden 的 wiki 说得很清楚:现在 HTTPS 基本是必需的,因为 Bitwarden 的网页保险库用到了浏览器的加密接口,大多数浏览器只在 HTTPS 环境下提供它们。用 http://NAS的IP:端口 打开,网页保险库没法正常工作。
官方推荐的做法是放在反向代理后面,由代理处理 HTTPS。证书要带完整的证书链,否则 Android 客户端可能连不上。
所以部署顺序是:先做好反向代理,见反向代理与 HTTPS,只在局域网用的话,用 DNS 质询申请证书,再回来部署 Vaultwarden。
部署
先决条件:反向代理(比如 Nginx Proxy Manager)已经在运行,并且你已经建好名叫 proxy 的共享网络,一个解析到它的域名,比如 vault.example.com。
服务 → Compose → 文件(Services → Compose → Files)→ 添加 → 从模板添加,选 vaultwarden,添加后 编辑。示例原样是:
yaml
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
ports:
- 80:80
volumes:
- CHANGE_TO_COMPOSE_DATA_PATH/vaultwarden:/data
restart: unless-stopped改成这样:
yaml
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
environment:
DOMAIN: "https://vault.example.com"
SIGNUPS_ALLOWED: "true"
volumes:
- ${{ sf:"appdata" }}/vaultwarden/data:/data
networks:
- proxy
restart: unless-stopped
networks:
proxy:
external: true- 去掉
ports:不再映射 80,也不映射别的端口,容器只能通过proxy网络从反向代理访问,局域网里别的设备绕不过代理。 DOMAIN:反向代理后面必须设,写你的 HTTPS 地址。Vaultwarden 需要知道自己是 HTTPS,附件等功能才能正常工作,不设的话,部分功能可能坏掉。SIGNUPS_ALLOWED:默认是开放注册。创建完你自己的账号之后,马上改成"false",不然陌生人也能在你的服务器上注册。- 数据目录放
appdata的子目录里。
在反向代理里添加主机:域名填 vault.example.com,转发主机名填 vaultwarden,转发端口填 80(容器内部的端口),申请证书并强制 SSL,打开 Websockets Support。
保存,检查,上线。
首次使用
- 打开
https://vault.example.com,点创建账户,填邮箱和一个很长的主密码。主密码只在你这里,服务器没有办法替你重置,忘了就是丢了。 - 创建好账号后,把 compose 文件里的
SIGNUPS_ALLOWED改成"false",保存,对这个应用 下线 再 上线,让容器按新配置重建。要让家人加入,临时打开注册,或者用管理页的邀请功能,用完立刻关掉。 - 在账户设置里开启两步验证,把恢复代码打印或抄下来,放在离线的地方。
- 在浏览器扩展和手机 App 里,选择自托管环境(Self-hosted),服务器 URL 填
https://vault.example.com,然后登录。 - 导出一份加密的 JSON 备份(保险库的导出功能),存到 U 盘里,离线保管,见下。
管理页:不需要就别开
Vaultwarden 的管理页(/admin)能看所有用户、删除用户。没设置 ADMIN_TOKEN 就是关闭的,不需要就保持关闭。
要开的话,先确保已经有 HTTPS,再用 Argon2 哈希过的令牌,不要写明文:
- 在 OMV 的 shell 里(root)生成哈希。容器在运行时:
omv-compose-run vaultwarden exec vaultwarden /vaultwarden hash,输入两次密码,输出以$argon2id$开头的一长串。 - 把它写进环境文件:编辑页勾选 显示环境文件(Show environment file),在 环境(Environment)框里写,整个值放进单引号:
bash
VAULTWARDEN_ADMIN_TOKEN='$argon2id$v=19$m=65540,t=3,p=4$<盐>$<哈希>'- 在 compose 文件的
environment里加一行引用它:
yaml
ADMIN_TOKEN: ${VAULTWARDEN_ADMIN_TOKEN}这么做是为了避开 $ 的陷阱:compose 文件里的 $ 会被当成变量。官方 wiki 的办法有两种,直接写在 compose 里要把每个 $ 写成 $$,用 .env 文件则要加单引号,本站选后一种,不容易写错。管理页的登录密码,是你生成哈希时输入的那个密码。第一次在管理页点 Save 之后,Vaultwarden 会在数据目录里生成 config.json,其中的值优先于环境变量。
备份
别用不停机的方式复制 SQLite 数据库
Vaultwarden 默认用 SQLite,db.sqlite3 在运行中被直接复制,可能得到损坏的拷贝。官方 wiki 还提醒:不要依赖文件系统或虚拟机快照作为备份。
- 影响范围:拷贝损坏时,密码库丢失,而且你要到恢复的那一天才知道。
- 前置条件:用 Compose 插件的默认备份,它会先停容器;不要勾选 跳过容器停止 / 启动,见更新与备份容器。或者用 Vaultwarden 自带的备份命令。
- 怎么回退:已经热备份过的,当作不可靠,立刻按下面的方式补一份。
数据目录里有什么
| 文件 | 是否备份 |
|---|---|
db.sqlite3(含 -wal、-shm) | 必须,几乎所有数据都在这里 |
attachments/ | 必须,文件附件 |
config.json | 建议。里面有明文的管理令牌、SMTP 凭据,备份要加密 |
rsa_key* | 建议。用来签发登录令牌,丢了只是所有人要重新登录,但私钥也敏感,备份要加密 |
sends/ | 可选。Send 的附件是临时性的 |
icon_cache/ | 可选,不值得备份 |
三种备份方式
- 插件的计划备份:数据目录很小,远小于默认的 1 GB 上限,会自动被包含。备份时容器会停几秒到几十秒,放在夜里。
- Vaultwarden 自带命令:在 OMV 的 shell 里(root)运行
omv-compose-run vaultwarden exec -T vaultwarden /vaultwarden backup。它用 SQLite 的VACUUM INTO在数据目录里生成db_<时间>.sqlite3,可以在运行中安全地执行。生成的文件会越积越多,要自己定期清理旧的。 - sqlite3 的
.backup命令:官方推荐的另一种在线备份方式,Docker 镜像里没有sqlite3,要装在宿主机上。
不管哪种,至少一份拷贝要放在别的地方,比如另一台机器或云存储。给备份加一层加密,尤其是里面有管理令牌的时候。方法见rsync 任务和异地与云端备份。
离线副本
服务器侧的备份再好,也别只靠一台 NAS。定期在网页保险库里导出加密的 JSON,存到 U 盘,放在别的地方。
恢复
先停止容器,再把备份的文件放回数据目录。用 .backup 或 VACUUM INTO 做的备份恢复时,要先删除数据目录里已有的 db.sqlite3-wal,否则陈旧的日志文件可能导致数据库损坏。如果你是连同 -wal 一起整份复制的,就必须成对地恢复。官方建议定期演练恢复,见恢复演练。
更新
Vaultwarden 会发布安全修复,要及时更新,但更新前先备份。手动更新:选中应用,拉取,上线,见更新与备份容器。
检查结果
https://vault.example.com能打开,证书有效,没有任何证书警告。- 注册页已经无法创建新账号。
- 在浏览器扩展和手机 App 里登录,在一端新增条目,另一端能同步到。
- 在 OMV 的 shell 里(root)运行
ss -tulpn | grep vaultwarden,没有宿主机端口,说明它只能通过代理访问。 - 运行一次计划备份,备份目录里有
db.sqlite3;并做过一次恢复演练。 - 启用了管理页的话,日志里没有「You are using a plain text ADMIN_TOKEN」的警告。
常见问题
网页保险库打不开,或者提示浏览器不支持加密功能。 你在用 http://。必须走 HTTPS,见上面「为什么必须 HTTPS」。
附件上传下载有问题。 检查 DOMAIN 是不是写成了你实际使用的 https:// 地址。
管理页登录失败,日志里有 variable is not set 之类的警告。 令牌里的 $ 被当成了变量。按上面的写法,值放进单引号,放在 .env 里。想确认,运行 omv-compose-run vaultwarden config,输出里的 ADMIN_TOKEN 里,$ 会显示成双写的 $$,这是 compose 的转义显示,是正常的。
手机 App 连不上,网页却可以。 证书链不完整,Android 默认不带 Let's Encrypt 的中间证书,要用完整链的证书。
日志里提示明文令牌不安全。ADMIN_TOKEN 不是哈希,或者你已经在管理页保存过配置,config.json 里还留着明文令牌。