GitHub推荐:66K stars,Raspberry Pi 能跑:Vaultwarden 是怎么把 Bitwarden 从 10 个容器压成 50MB 单二进制的
一句话总结
用单个 Rust 二进制(~50–100MB RAM)重新实现 Bitwarden 官方服务端(10+ Docker 容器、≥1.5–2GB RAM),让官方 Bitwarden 浏览器/桌面/移动客户端零修改连接到一个能跑在 Raspberry Pi 上的自托管密码管理服务端。
值得关注的理由
- 极致的资源换 UX — 100× RAM 优势换来”官方客户端 UX + 自托管”,填补”Bitwarden 太重、KeePass 又太本地化”的空白象限,是当前自托管密码管理器赛道 66k★ 的事实标准。
- 架构层面的工程样本 — Rocket 0.5(异步)+ Diesel(同步)的桥接、Diesel
MultiConnection横跨 SQLite/MySQL/PostgreSQL、OpenDAL 抽象本地/S3、make_config!宏把 200+ 配置项编进类型系统 —— 这一整套都是中型 Rust 后端的优秀范例。 - 「独立却受雇于官方」的诡异治理样本 — 主作者 Daniel García 同时受雇于 Bitwarden 公司去做一个与官方服务端并行的非官方替代实现(AGPL-3.0-only),8.5 年来维持「兼容性 > 特性完整」「承认上游依赖」的克制路线,是研究 open-source/community-driven vs company-driven 张力的活教材。
项目展示
仅 logo + contributors 合成图 —— 项目本身没有 demo gif / 截图(其价值在于”跑得起来”,肉眼不可见);UI 完全复用官方 Bitwarden 客户端。
项目画像
| 维度 | 数据 |
|---|---|
| GitHub | https://github.com/dani-garcia/vaultwarden |
| Star / Fork | 66,112 / 3,138(Watchers: 304) |
| 代码行数 | 71,085(Rust 39.6% / JavaScript 30.1% / CSS 16.6% / JSON 5.2% / Handlebars 2.5% / TypeScript 2.1% / SQL 2.0%) |
| 项目年龄 | 102.5 个月(2018-02 首版 → 2026-08 仍在更新) |
| 开发阶段 | 稳定维护(近 365 天 176 commit,月均 ~15) |
| 贡献模式 | 单人主导 + 核心三人组(Daniel 26.6% / BlackDex / jjlin / stefan0xC,合计 ~37%) |
| 热度定位 | 大众热门 × 细分赛道头部(自托管 Bitwarden 客户端生态的事实服务端入口) |
| 质量评级 | 代码优秀 / 文档良好 / 测试不足 |
| License | AGPL-3.0-only |
作者视角:为什么存在这个项目
创始人/作者背景
Daniel García,Asturias 西班牙独立开发者,2011 年注册 GitHub,账号 15 年。15.4 年账号、1,313 粉丝、28 个公开仓库,vaultwarden 在他所有仓库里以 66k★ vs 次高 387★ 的悬殊比排名第一(投入权重极高)。关键身份设定:他目前受雇于 Bitwarden Inc.(官方母公司),但同时维护这个「非官方替代服务端」—— README 第 136 行明确这种贡献与 Bitwarden 公司保持独立、并由其他维护者审查,这是一种罕见的”内部人对抗自家产品”位置。
问题判断
Bitwarden 在 2017 年把服务端从 C#/Mono 改写为 .NET Core,但自托管栈仍然是 10+ Docker 容器、≥1.5–2GB RAM、必须配合 MSSQL/PostgreSQL —— 对于在 Raspberry Pi / NAS / 小型 VPS 上想自托管密码管理器的个人/家庭用户来说,资源占用与运维复杂度都 over-engineered。其他自托管密码管理器要么没有 Bitwarden 客户端那样的浏览器自动填充 UX(KeePass),要么是为团队协作设计(Passbolt),要么根本不是密码管理器形态(Keycloak)。Daniel 看到的市场缺口是:让市场上 UX 最好的 Bitwarden 客户端,能跑在 100 元的小服务器上。
解法哲学
① 资源最小化是首要约束 —— Rust + 单二进制 + SQLite 默认 + Alpine/MUSL 静态链接,最终镜像能跑 RPi Zero。
② 客户端兼容性 > 服务端特性完整 —— 不重写客户端 UI,官方客户端改 API 就跟着改服务端;2021 年把项目从 bitwarden_rs 改名 vaultwarden 就是为了跟 Bitwarden 商标保持距离(README:146–147)。
③ 明确不做什么的清单 —— 官方 Bitwarden 的 enterprise SSO/Directory Sync 等特性 Vaultwarden 故意不做(Issue #246 META「won’t add unless contributed」),仅通过 OpenID Connect 提供有限度的 SSO。
④ 承认上游依赖 —— README + Wiki 反复请求用户给 Bitwarden 付费以支撑上游工作,这是 open-source 项目中罕见的”指示竞争对手付费”的姿态。
战略意图
是 Daniel 的旗舰核心产品(非基础设施),没有商业意图(AGPL-3.0-only + 无 SaaS/企业版 + 无 open-core 拆分),社区驱动(贡献集中度 26.6%,前 30 人合计,主贡献者排第一),与官方 Bitwarden 路线图反向而行(社区驱动 vs 公司驱动)。
全数据基于 GitHub Repo 元数据 + README + Wiki + DeepWiki,无独立作者博客。
核心价值提炼
创新之处
- 用 Rocket 异步 + Diesel 同步桥接模式跑请求/响应(实用 5/5)
src/db/mod.rs:30-42的run_blocking包同步 DB 调用进tokio::spawn_blocking;DbConnrequest guard 自实现Arc<Mutex<Option<PooledConnection>>>+ 自定义Drop异步归还连接;用 semaphore 限制最大并发防止池饥饿。
- Diesel
MultiConnectionderive 横跨三套 SQL 方言(实用 4/5,可迁移 3/5)- 单 codebase 跑 SQLite/MySQL/PostgreSQL;代价是 56+55+46 份手写 SQL 迁移(按方言分裂)+ schema 字段类型差异需要
#![cfg]分支处理。
- 单 codebase 跑 SQLite/MySQL/PostgreSQL;代价是 56+55+46 份手写 SQL 迁移(按方言分裂)+ schema 字段类型差异需要
- OpenDAL +
s3://前缀检测做本地/S3 透明切换(新颖 3/5,实用 4/5)- 单
Operator类型 +dashmap缓存 Operator,URL 前缀决定走services-fs还是services-s3;S3 模式下用 reqwest 解析 path segments 的join_path/parent/file_name是手写的简陋 polyfill。
- 单
make_config!声明宏把 200+ 配置项编进类型系统(新颖 4/5,实用 4/5,可迁移 3/5)- 每个字段同时展开成 ConfigBuilder / getter / admin panel ElementData / privacy mask / env var name;
def/auto/option/generated四种「缺值行为」配合pastey::paste!把 env var 大写转换;新增SSO_FOO不写 default 会编译报错 —— 用类型系统保证 config schema 完整性的极致体现。
- 每个字段同时展开成 ConfigBuilder / getter / admin panel ElementData / privacy mask / env var name;
- proc-macro crate
macros/做UuidFromParam/IdFromParamderive(实用 4/5,可迁移高)- syn+quote 给 newtype ID 自动实现 Rocket
FromParamtrait,消除 30+ ID 类型的样板代码。
- syn+quote 给 newtype ID 自动实现 Rocket
- web-vault digest pinning + 多架构 Docker 矩阵(amd64/arm64/armv7/armv6)(新颖 3/5,实用 5/5)
- Dockerfile 用
FROM docker.io/vaultwarden/web-vault@{{ vault_image_digest }} AS vault而非 tag,supply chain attack 面显著缩小;4 个架构 × 2 个 base = 8 个镜像变体,靠DockerSettings.yaml+Dockerfile.j2(Jinja2) +docker-bake.hcl三件套的声明式渲染支撑。
- Dockerfile 用
- WebAuthn/U2F/Passkey 三阶段迁移(新颖 3/5,实用 3/5)
- 启动期跑
migrate_u2f_to_webauthn+migrate_credential_to_passkey(main.rs:92-93),用webauthn-rs的danger-allow-state-serialisation把 state 序列化进 DB(Cargo.toml:155–156 注释解释为何开启 danger feature)。
- 启动期跑
可复用的模式与技巧
| 模式 | 适用场景 |
|---|---|
Rocket 0.5 异步 + Diesel 同步桥接(run_blocking + DbConn 自实现归还) | 任何”Rocket + 同步 ORM”项目 |
Diesel MultiConnection derive + 手写 DbConnManager | 一个 codebase 需要跑多种 SQL 方言 |
make_config! 声明宏 + admin panel 自动生成 | 配置项 ≥ 50 且需要 admin web self-service |
| OpenDAL + scheme 前缀路由 | 本地 ↔ S3 零代码切换的 Rust 后端 |
proc-macro crate 做 FromParam derive | Rocket + 多 newtype ID 项目 |
DockerSettings.yaml + Dockerfile.j2 + docker-bake.hcl 三件套 | 需要给多架构(尤其 ARMv6/v7)发布镜像 |
| web-vault digest pinning + 手动升级 | 任何消费上游 container image 的项目 |
build.rs 把 git tag/commit 编进二进制 | 几乎所有 Rust CLI / server |
关键设计决策
-
决策:Rocket + Diesel + 自实现
DbConn桥接- 问题:Rocket 是异步框架,Diesel 是同步阻塞 ORM,桥接是非典型需求
- 方案:
run_blocking包装 +DbConnrequest guard +semaphore限流 - Trade-off:失去 Diesel 原生 async story(Diesel 2.2 async 才出现,Vaultwarden 已定型无法回头),代码复杂度上升
- 可迁移性:中
-
决策:用
bitschema公开端点路径与 JSON 字段命名强制保持 camelCase- 问题:官方 Bitwarden 客户端严格按 endpoint path + JSON field name 校验
- 方案:
#[serde(rename_all = "camelCase")]全仓统一 + 路由路径常量集中在api/core/*每个文件里 - Trade-off:牺牲了 Rust 习惯的 snake_case 命名一致性,换来与 .NET 官方服务端字节级兼容
- 可迁移性:中
-
决策:Docker 多架构通过声明式三件套(yaml + j2 + bake)
- 问题:4 架构 × 2 base = 8 个镜像变体,手工 Dockerfile 不可维护
- 方案:
DockerSettings.yaml是数据(架构列表 / digest / 镜像 tag),Dockerfile.j2是模板,docker/Makefile用 Jinja2 渲染成Dockerfile.debian/.alpine,最后docker buildx bake跑docker-bake.hcl做并行构建 - Trade-off:引入 Jinja2 + bake + Makefile 三层间接,每次新增 arch 要改三个文件;但 ARMv6 (RPi Zero) 支持是用户群核心诉求
- 可迁移性:中
-
决策:
SUPPORTED_FEATURE_FLAGS从 Bitwarden 4 个官方仓库手动 sync + 注释锚点- 问题:客户端每加一个新 feature flag,服务端不识别就断开
- 方案:
config.rs:1413-1417列出 4 个 Bitwarden 仓库的 sync 锚点,每加一个 flag 三方合并 - Trade-off:手动维护成本高,但避免了”自动 sync 突然引入未审计代码”的 supply chain 风险
- 可迁移性:中
竞品格局与定位
竞品对比矩阵
| 维度 | Vaultwarden | 官方 Bitwarden (self-host) | Passbolt | KeePassXC + Syncthing | Keycloak |
|---|---|---|---|---|---|
| 客户端 UX | ★★★★★(官方客户端零修改) | ★★★★★(自己客户端) | ★★★(桌面扩展尚可) | ★★★(桌面强,浏览器/移动端弱) | ★(无密码管理器形态) |
| 自托管 RAM | 50–100 MB | 1.5–2 GB+ | ~300 MB | 0(无服务端) | 300–600 MB |
| 部署复杂度 | 单 container,1 分钟 | 10+ container,30 分钟起步 | 中等 | 客户端 + 自配同步 | 中等 |
| Enterprise 特性 | ❌ 故意不做(无 SSO/Directory Connect/SCIM) | ✅ 全套 | ✅ 团队 sharing + OpenPGP | ❌ | ✅ IAM 全套 |
| 安全审计 | ⚠️ 无 Cure53 级别 | ✅ Cure53 定期公开报告 | ⚠️ | ✅ 客户端独立审计 | ✅ |
| 商业支持 / SLA | ❌ | ✅ | ✅ | ❌ | ✅ |
| 多设备同步 | ✅ 即时(服务端中介) | ✅ 即时 | ✅ | ⚠️(需自接 Syncthing/Dropbox) | ✅ |
| 浏览器自动填充 | ✅(官方浏览器扩展直连) | ✅ | ⚠️ | ⚠️ | ❌ |
差异化护城河
- 生态护城河(最强):让 Bitwarden 客户端生态(移动/桌面/浏览器扩展/CLI/1Password importer/家族共享)全免费可用——这是 KeePass/Passbolt/ProtonPass 都打不进去的护城河。客户端兼容性本身才是核心资产,不是 Vaultwarden 代码本身。
- 技术护城河(弱):任何有时间的 Rust 开发者都能重写(Vaultwarden 已经证明了)。
- 信任护城河(中):AGPL + 大量用户 + Daniel 受雇于 Bitwarden 这种”半官方”地位带来隐性背书;但缺 Cure53 审计是真硬伤(Reddit 2025-05 两年使用评论明确点出此结构性风险)。
竞争风险
- 最危险:官方 Bitwarden 如果某天推出「轻量级 self-host」(比如基于 Bitwarden 的 Rust 重写),Vaultwarden 立刻边缘化——这也是 Daniel 受雇于 Bitwarden 这种暧昧关系既是优势也是定时炸弹的原因(issue #246 评论中已出现”are you going to be replaced by your employer”讨论)。
- 次要:Bitwarden 客户端 API breaking change 速率加快(参考 #7607、#4870、#687),Vaultwarden 跟不上节奏会失去「兼容性」核心卖点。
- 边缘:Passbolt 个人 UX 改善、KeePassXC 浏览器扩展补强,会蚕食相邻象限,但不会消灭 Vaultwarden 的”Bitwarden 客户端生态中介”身份。
生态定位
「轻量级自托管密码管理器的默认选项」 —— 不挑战官方商业版、不挑战 1Password 商业 SaaS,只占据「官方太重 + KeePass 太本地」之间的甜蜜点。
自托管密码管理赛道中无明显同象限挑战者。
套利机会分析
- 信息差:零 —— 已是赛道绝对头部(仅次于官方 Bitwarden 客户端生态);价值在「运营效率和设计哲学借鉴」而非”被发现”。
- 技术借鉴:
- Rocket + Diesel 桥接:
run_blocking+DbConn自实现归还(任何 Rocket + 同步 ORM 项目) make_config!声明宏把配置编进类型系统(任何中型 Rust 后端)- 多 DB 方言支持:Diesel
MultiConnection+ 手写DbConnManager+ schema#[cfg]分支 - OpenDAL +
s3://scheme 路由做本地/S3 透明切换 - Docker 多架构矩阵的三件套(yaml + j2 + bake)
- proc-macro crate 消除 newtype ID 样板代码
- Rocket + Diesel 桥接:
- 生态位:在「Bitwarden 客户端体验 × 自托管 × 极低资源占用」象限是唯一存在 —— KeePass 太本地、Passbolt 偏团队、官方太重。
- 趋势判断:
- Vaultwarden 本身已 不在增长高峰(2022-Q4 ~ 117 commit/月的峰值后回落到月均 ~15),但 Bitwarden 客户端生态持续扩张(ProtonPass / 1Password 没在侵蚀),它占据的象限中长期有需求。
- 后发劣势:任何后来者想做类似项目,必须面对 8.5 年 + 66k★ + 已被 Docker Hub / LinuxServer.io 收录的事实标准壁垒;但Rust 生态今天做这种项目比 2018 年容易得多(actix-web/axum/Rocket 都成熟、Diesel 2.x async 也快了),后发者通过「官方 Bitwarden 客户端兼容 + Cure53 审计 + 价格更低 SaaS」组合有机会。
- 后发优势:Vaultwarden 的架构债务(Diesel 同步 + Rocket 异步桥接)已成包袱,新项目可以用 sqlx + axum 全部 async 重写一遍。
风险与不足
- 可持续性风险(最大):官方 Bitwarden 客户端 API 每次大版本(2024.8 / 2026.8…)都会引发一波 break-fix 周期(issue #7607 119 评论、#4870、#687 三连击)—— 这是 Vaultwarden 的结构性「被动追赶」,任何一个客户端 API breaking change 都可能让 Vaultwarden 数周内无法与新客户端配对;随 Bitwarden 加快 release cadence,这种风险会持续加剧。
- 代码异味:
src/api/core/ciphers.rs单文件 2223 行 + 75 个函数;organizations.rs3253 行;accounts.rs1831 行 —— 典型「API 文件作为 god module」模式;api/identity.rs一个login函数 70+ 行处理 6 种 grant_type(line 60–126),内嵌 if-else tree 难以拆解。 - 过度工程:
make_config!宏本身 +pasteyproc-macro +derive_more5 个 feature 同时开 —— 对 maintainer 是抽象税,对贡献者学习曲线陡。 - 欠工程 — 测试明显不足:单元测试
#[test]在 src/ 里仅 ~35 次(http_client.rs 24 个 + storage.rs 4 个 + 其他零星);integration 测试完全没有 src/tests/ 目录;playwright/ 13 个 E2E 文件需要真实 SMTP/SSO 后端;密码学 / 权限检查 / 多 DB 方言一致性测试几乎为零。 - 安全审计缺失:无 Cure53 级别独立审计报告(Reddit 2025-05 两年使用评论明确点出此为结构性风险)。密码管理器是「存所有用户的最高敏感凭证」的服务,无第三方审计对 66k★ 规模是相对少见的承担。
- license 风险:AGPL-3.0-only 限制商业二次开发;web-vault 部分是 Bitwarden 官方 AGPL,但 bw_web_builds 仓库有不同 license,混合 license 在分发时需要小心。
- 架构债务:Diesel + 同步 ORM + Rocket 0.5 异步桥接是历史包袱;今天重写应用 sqlx + axum 全 async,但 Vaultwarden 已定型无法回头。
行动建议
如果你要用它
- 个人/家庭自托管:✅ 首选 Vaultwarden(docker-compose.yml 一次拉起,SQLite 默认,50–100MB RAM)。比官方栈小 100×,比 KeePass 体验强 10×。
- 企业/合规需求:❌ 用官方 Bitwarden self-host(Cure53 审计 + 商业支持 + Enterprise SSO/Directory Connect/SCIM 完整)—— Vaultwarden 明确不做企业特性。
- 多设备同步 + 浏览器自动填充:✅ Vaultwarden;备选 Bitwarden 官方或 1Password。
- 极简本地优先 / 气隙部署:⚠️ 用 KeePassXC(Vaultwarden 需要服务端,违背 air-gap 场景)。
如果你要学它
按以下顺序阅读源码:
src/main.rs(~500 行):启动期做了哪些事(feature flag 同步 / WebAuthn 迁移 / panic hook / Rocket mount)。src/config.rs的make_config!块(行 502–937):学习把配置 schema 编进类型系统的极致范例。src/db/mod.rs:Rocket 异步 + Diesel 同步的桥接、MultiConnectionderive 用法、自实现DbConn生命周期。src/storage.rs:OpenDAL 抽象 + scheme 前缀路由的实战。src/api/core/ciphers.rs:体验单个 API 文件如何变成 god module(2223 行),作为反例思考如何在自己的项目避免。docker/DockerSettings.yaml+docker/Dockerfile.j2+docker/docker-bake.hcl:多架构 Docker 矩阵的声明式三件套。macros/src/lib.rs:proc-macro crate 实战。
如果你要 fork 它
可改进的方向:
- 测试覆盖补齐:补单元测试 + integration tests + 多 DB 方言一致性 harness(最值得的改造,PR 会被欢迎)。
- 架构 async 化:等 Diesel 2.2 async 稳定后,迁到 sqlx + axum 去掉
run_blocking桥接的复杂度。 - 拆分 god module:把
src/api/core/ciphers.rs/organizations.rs/accounts.rs按子领域拆分(纯重构、不动行为)。 - 加 Cure53 级别审计:找独立安全公司做完整 audit 并公开报告(社区募资+ Bitwarden 资助是可能的路径)。
- 加官方 Bitwarden 未实现但用户长期请求的特性:device approval flow、master password re-prompt 策略、可选 enterprise 路径(edge 部署)—— 但需要先解决”会不会被官方母公司挤压”的治理问题。
知识入口
| 资源 | 链接 |
|---|---|
| DeepWiki | https://deepwiki.com/dani-garcia/vaultwarden |
| Zread.ai | 未收录 |
| 关联论文 | 无(密码管理器服务端,无学术论文入口) |
| 在线 Demo | 无(需自部署;上游 Bitwarden 提供 vault.bitwarden.com 试用) |
| 外部独立分析 | Reddit r/selfhosted 2025-04 Vaultwarden vs Bitwarden Self-hosting — 独立观点:“装完才发现是 Bitwarden 客户端兼容性维护赛跑”。 Reddit r/selfhosted 2025-05 Is Vaultwarden Worth It 2025? — 两年使用诚实话:崩溃/RPi sync 失效/反向代理/ADMIN_TOKEN 持久化 + 无 Cure53 审计风险。 |