导火索很简单:1Password 又涨价了。
我的三人 Families 套餐以前是 US$59.85/年,账单页面显示下一周期会变成 US$71.88/年,约上涨 20%。这个价格本身不算离谱,但我实在不想继续跟着订阅价格走了。于是开始找平替,最后把个人密码库迁到了 VPS 上自建的 Vaultwarden。
迁移本身并不难,真正费时间的是迁移前后那一圈容易被忽略的事:Passkey 和普通登录条目的处理方式不同,服务端坏了会把恢复凭据一起锁住,批量整理还可能触发版本冲突。
这篇文章记录一次实际迁移,部署过程只保留与迁移相关的部分。文中的真实域名用 vault.example.com 代替。
先说结论 #
- 对个人或家庭使用,Vaultwarden 足够轻量,官方 Bitwarden 的浏览器、桌面和移动端客户端都可以继续使用。
- 密码、用户名、OTP、备注等常规数据迁移顺利;安卓端按应用内导入流程可以完整迁移,Passkey 也在这次实测中成功导入。
- 这次没有使用
.1pux或其他导出文件,全部通过安卓端直接从其他 App 导入;常规数据和 Passkey 都完整迁移成功。 - 1Password 不应该在第一次导入成功后立刻删除。真正的完成标准是:新客户端能用、备份能读、恢复路径不依赖这个密码库本身。
flowchart LR
A["1Password"] --> B["Export / CXP"]
B --> C["Vaultwarden"]
C --> D["Client verification"]
C --> E["Backups"]
D --> F["Keep 1Password until verified"]
为什么选 Vaultwarden #
当时对比过三条路线:
- Vaultwarden:轻量、Bitwarden 客户端生态完整,适合个人和家庭;服务端采用独立实现,兼容性需要按实际版本验证。
- Passbolt:团队权限、审计和共享能力更完整,但部署和配套更重。
- KeePassXC:服务器信任面最小,但本质是同步数据库文件,多人实时共享不如前两者自然。
我已经有一台 VPS 和 1Panel,所以最后选了 Vaultwarden:数据放在独立的数据盘,Docker 运行,继续复用 1Panel/OpenResty 的 HTTPS 和反向代理。
部署中最容易低估的工作 #
1. 数据盘和目录先定下来 #
VPS 上新增了一块 150 GiB 的盘,挂载到 /data,Vaultwarden 数据目录固定为:
/data/services/vaultwarden文件系统最后选了 ext4。中间实际比较过 XFS:它在空盘上能多出一些可用空间,但不能缩容;考虑到以后可能换更小的盘,最终还是把“恢复和迁移的余地”放在了“多省一点空间”前面。
这个选择没有普适答案,但密码库这种服务不值得为了理论性能把未来的迁移路径堵死。
2. DNS、证书和反代比启动容器更容易出问题 #
真正接入公网前,先确认 DNS 已经指向正确的 VPS。过程中曾经遇到旧 IPv6 记录导致请求没有到新机器;容器本身没问题,问题在入口。
最后让 1Panel 管理 vault.example.com 的证书,用现有 Cloudflare DNS 账号完成签发和续期,再由 OpenResty 反代到 Vaultwarden。这样证书只有一个管理入口,不再让独立 certbot 和面板同时维护同一份证书。
验收时不能只看容器 healthy:还要从公网检查 HTTPS、自动跳转、Vaultwarden 的健康接口和实际登录。后来有一次访问失败,最终查明是本机 DNS 的瞬时异常,远端服务一直是 200 和 healthy。
3. 关闭公开注册,邀请和注册分开控制 #
第一次部署时我把公开注册和邀请都关了。需要和朋友共享时,我保持公开注册关闭,只打开邀请:
SIGNUPS_ALLOWED=false
INVITATIONS_ALLOWED=true朋友使用自己的邮箱和主密码注册,再加入 Organization 和 Collection。个人密码留在自己的库里,需要共享的条目才放到共享集合中。
邀请邮件还需要 SMTP。这里用 Mailgun 的 SMTP 做了两层验证:先做 STARTTLS 登录握手,再发送真实邀请邮件。前者成功不等于后者一定投递成功,这两个结果要分开记录。
迁移的实际路线 #
第一步:先保留 1Password #
迁移前先确认 1Password 还能正常解锁,并准备一个加密备份。不要把“新服务能登录”误认为“旧数据已经安全迁完”。
第二步:在安卓 App 内直接导入 #
这次没有使用 .1pux,也没有先导出文件。全部迁移都在安卓端 Bitwarden App 内完成:先配置自托管服务器,再进入:
设置 → 密码库 → 导入项目 → 从其他 App 导入 → 1Password
这条路径把常规数据和 Passkey 一起完整导入了。整个迁移依靠客户端提供的应用间导入,没有经过文件导出。
导入后至少检查这些内容:
- 登录名和密码是否完整;
- OTP 是否能生成正确验证码;
- 备注、自定义字段和附件是否还在;
- 文件夹是否需要重新规划;
- 是否误导入了第二份,产生重复条目。
首次操作仍建议先抽查少量关键条目,确认新设备登录、自动填充和 Passkey 正常后,再处理全部数据;在确认前保留旧的 1Password。
相关说明:
第三步:客户端接入要单独验收 #
浏览器扩展、桌面端和手机 App 都要在登录前选择自托管服务器,地址类似:
https://vault.example.com我还试了 Bitwarden CLI。配置自托管地址的最小路径是:
bw config server https://vault.example.com
bw config server
bw login your-email@example.com
bw status这里遇到过一个与服务端无关的坑:当时用 pnpm 全局安装的 CLI 在 Node 26.3.0 下报 Cannot find module 'buffer/'。最后确认是全局依赖链接和运行时组合的问题,改用 Node 22 配合 npm 安装同版本 CLI 后恢复正常。
所以 CLI 报错时,先检查 bw 的安装方式、Node 版本和实际服务地址,不要一上来重启 Vaultwarden。
数据整理给了一个很实际的教训 #
迁移完成后,我还想把原来全部位于 No Folder 的 261 条记录重新分类。分类本身不难,难的是批量更新。
我先做了加密备份,然后只修改文件夹归属,不改密码、备注、OTP,也不删除重复项。批量并发更新时,Vaultwarden 的版本竞争造成 6 条同名 Mattermost 条目暂时进入回收站。最后通过重新同步和逐条恢复,条目 ID 与备份完全对回来了。
这件事留下了几条规则:
- 批量写入前必须有可恢复备份;
- 只改一个字段,也要把它当作真实数据变更;
- 并发请求不一定更快,客户端副本过旧时反而会制造竞态;
- 最终校验不能只看 HTTP 200,要重新对比总数、条目 ID 和回收站。
现在的密码库按工作与内网、开发与云服务、个人账号、通信与社交、金融与实名、家庭与设备、安全与密钥分类,261 条都已经离开 No Folder。重复项没有擅自删除,留给之后人工确认。
备份要能拿来恢复 #
自建密码库最大的心理变化,是你要自己承担恢复责任。
这次的收口方式是:
- 数据库、附件和配置放在
/data下; - 本机定期生成备份;
- 本机备份成功后,再通过 SSH 同步到另一台主机
DO_NEW; - 备份文件使用加密格式,并保留可核对的结果;
- 云主机、DNS、邮箱和 VPS 的恢复凭据不全部放进这个密码库,至少保留一条独立的 break-glass 路径。
只把备份放在同一台 VPS 上,不算真正的异地备份。更重要的是,备份存在也不等于能恢复:至少要做一次临时恢复,确认数据库、附件、配置和客户端登录都能回来。
最后的判断 #
这次迁移让我确认,Vaultwarden 可以作为个人或家庭场景下的 1Password 替代品,容器启动只是整个过程的第一步。
你放弃一部分托管服务,同时接手 DNS、TLS、SMTP、升级、日志、备份和恢复。普通密码数据迁移的门槛不高,Passkey、共享权限和故障恢复才是需要重点投入的部分。
如果只是为了省订阅费,而不愿意维护备份和恢复流程,迁移的收益可能不值得成本;如果更在意数据控制、客户端自由度,并愿意把它当作一个小型生产服务来维护,Vaultwarden 的确是一条很实用的路。
我把下面四件事都确认后,才把迁移视为完成:
- 新设备能登录并自动填充;
- OTP 和关键附件经过抽查;
- Passkey 已在目标客户端验证,或有明确的逐站重建计划;
- VPS 挂掉后,仍有独立的恢复入口和可验证备份。