自建密码库 Vaultwarden(2026 实操):Docker 起服务,客户端照用官方 Bitwarden

密码存浏览器里最省事,可换一台设备就开始抓瞎;存某个笔记 App 里,导出一次能折腾半小时。Bitwarden 的客户端我一直用着舒服,服务端又不是非用它的云——Vaultwarden 用 Rust 把同一套 API 重写了一遍,小配置的机器就能跑,浏览器插件、桌面端、手机 App 全部照常连,地址换成自己的。

这篇就把它装起来,重点是那几个不装不知道、装完才发现的地方:域名得带 https、反代得传 Upgrade、注册得先开再关。

先说清适用范围

适合自己用,或者家里几个人、小团队共用。数据在自己的磁盘上,导出、迁移、备份都是你自己的事。

不适合想省心的人,也不适合要企业合规审计的场景。另外有个硬前提:必须有域名和 HTTPS。Bitwarden 客户端不接受 http 地址,这一点没有开关可以绕过,所以下面反代那步不能跳。

前置条件

  • 一台能跑 Docker 的 VPS,1C1G 的小机器足够,Vaultwarden 内存占用比 PHP 站低得多
  • 域名解析到这台机器,比如 vault.example.com
  • Nginx 或者宝塔面板,用来做 TLS 终止
  • 已知的 Docker Compose 基本操作

第一步:起容器

mkdir -p /opt/vaultwarden && cd /opt/vaultwarden

新建 compose.yaml,把域名换成你自己的:

services:
  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: unless-stopped
    environment:
      DOMAIN: "https://vault.example.com"
      SIGNUPS_ALLOWED: "true"
      ADMIN_TOKEN: "换成一长串随机字符,比如 openssl rand -base64 32 的输出"
    volumes:
      - ./vw-data:/data
    ports:
      - "127.0.0.1:8000:80"

端口只绑 127.0.0.1,外面一律走反代,8000 不要直接暴露到公网。SIGNUPS_ALLOWED 这里先开成 true,等自己账号注册完再关,顺序反了就麻烦。

docker compose up -d
curl -s http://127.0.0.1:8000/alive

第二条命令应该返回一串 Unix 时间戳,比如 1761…。返回了这个,说明服务本身起来了,剩下的工作全在 Nginx。

第二步:Nginx 反代加证书

upstream vaultwarden {
    zone vaultwarden 64k;
    server 127.0.0.1:8000;
    keepalive 2;
}

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      "";
}

server {
    listen 80;
    server_name vault.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    http2 on;
    server_name vault.example.com;

    ssl_certificate     /etc/letsencrypt/live/vault.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/vault.example.com/privkey.pem;

    client_max_body_size 525M;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    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 / {
        proxy_pass http://vaultwarden;
    }
}

老一点的 Nginx 没有 http2 on; 这条指令,把 listen 443 ssl; 改成 listen 443 ssl http2; 就行,版本上以你的环境为准。

宝塔用户建好站点后手动补三样东西:client_max_body_size、Upgrade 那两行、还有 X-Real-IP。少任何一行,下面「常见坑」里对应的问题都会出现。

证书用面板的一键 Let's Encrypt,或者 certbot --nginx -d vault.example.com。签发完 nginx -t && nginx -s reload。

第三步:注册自己,然后关掉注册

客户端里选「自建服务器」,地址填 https://vault.example.com,注册一个账号,随便存两条密码验证一下。

确认能用之后,把 compose 里的 SIGNUPS_ALLOWED 改成 "false",重建容器:

docker compose up -d

以后要加人,走 https://vault.example.com/admin 的 Users 页面发邀请,而不是把注册重新打开。ADMIN_TOKEN 就是这个页面的钥匙,放在邮箱或者聊天记录里都算泄露。

想要更稳一点,可以用 Argon2 哈希代替明文 token,登录时提交原文:

docker run --rm -it vaultwarden/server /vaultwarden hash

生成的 $argon2id$v=19$… 串填进 ADMIN_TOKEN 时,compose 文件里的 $ 全都要写成 $$,不然会被当成变量替换掉,结果就是怎么登都提示 invalid admin token。

怎么确认真的生效

curl -s -o /dev/null -w "%{http_code}\n" https://vault.example.com
docker logs --tail 20 vaultwarden

第一条返回 200,日志里没有反复刷的错误就够了。再做一个动作测试:网页端改一条密码,掏出手机看它几秒内变不变。变了说明 WebSocket 通了;几十秒都不动,回去查 Upgrade 和 Connection 那两个头。

容易踩的坑

域名少了 https://。 DOMAIN 必须是完整带协议的地址,写 vault.example.com 会让附件下载和图标请求走错方向,报的错还很含糊。

反代没传 Upgrade 头。 表现不是打不开,而是「能登上去,但改的东西不同步」,很容易误判成客户端 bug。

上传附件 413。 Nginx 默认 client_max_body_size 1m,传个几百 K 的附件就被拦了,525M 那种大备份更是没戏。

先关注册后注册。 自己都进不去的时候,可以临时把 SIGNUPS_ALLOWED 打开重启,或者改 INVITATIONS_ALLOWED 走邀请,别急着重装。

备份只拷了 sqlite 文件。 /data 里的 db.sqlite3、attachments、sends、rsa_key*、icon_cache 是一整套,缺了密钥文件,附件就解不开。最稳的做法是 docker compose stop 之后整目录打包,跟着其他东西一起扔到异地,和我之前写的全站备份那套可以共用一份 rclone 配置。

想推翻重来

docker compose down 停掉容器、数据目录原封不动,想过两天再研究随时 up -d 回来。真正不可逆的只有删掉 vw-data 那一步,动手前先确认异地那份备份能解开。

自建服务这类东西,装起来其实十几分钟,后面的活都在备份、证书续期和偶尔一次升级上。同一套路子我还写过评论系统 Artalk、网站统计 Umami 和监控告警 Uptime Kuma,都是 Docker 起服务加一层反代,配置可以互相抄。

© 版权声明
THE END
喜欢就支持一下吧
点赞10 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容