在数字化时代,每个人都在数十甚至上百个网站上拥有账号,密码管理已经不再是可选技能,而是每个互联网用户的刚需。市面上的密码管理器不少——1Password、LastPass、Bitwarden……但如果你是一名 VPS 玩家,手里有闲置的服务器资源,为什么不自己搭一个?
今天这篇文章,我们就来完整实战一遍:在一台 VPS 上用 Vaultwarden(原 Bitwarden_RS)自建私有密码管理器。从 Docker 部署、Nginx 反代、HTTPS 证书,到安全加固、备份策略、客户端配置,一条龙搞定。
为什么选择 Vaultwarden 而不是官方 Bitwarden?
Bitwarden 是目前最流行的开源密码管理器之一,官方提供了自托管版本。但官方自托管版本使用 C# 编写,依赖 SQL Server、.NET Core 等重量级组件,光是启动就至少需要 2GB 内存,对于大多数小 VPS 来说根本跑不动。
Vaultwarden(原名 Bitwarden_RS)是用 Rust 重写的非官方 Bitwarden 服务端实现,它 100% 兼容 Bitwarden 官方客户端 API,但资源消耗极低——空闲时仅占用 10-20MB 内存,一台 512MB 的小鸡就能轻松运行。两者对比如下:
| 特性 | 官方 Bitwarden | Vaultwarden |
|---|---|---|
| 编写语言 | C# / .NET | Rust |
| 最低内存 | 2GB+ | 10-20MB |
| 数据库 | SQL Server / PostgreSQL | SQLite / MySQL / PostgreSQL |
| 客户端兼容 | 官方客户端 | 官方客户端完全兼容 |
| 部署复杂度 | 高(多容器编排) | 低(单容器) |
| 高级功能 | 组织、企业功能完整 | 大部分功能支持,部分企业功能有限 |
对于个人用户和小团队来说,Vaultwarden 是更务实的选择:资源占用极小、部署简单、更新频繁、社区活跃。唯一需要注意的是,Vaultwarden 默认使用 SQLite 数据库,虽然性能对于个人使用绰绰有余,但如果你有数百人同时使用的场景,建议切换到 MySQL 或 PostgreSQL。
环境准备与基础部署
系统要求
在开始之前,确保你的 VPS 满足以下最低要求:
- 操作系统:Ubuntu 22.04/24.04 或 Debian 11/12(推荐)
- 内存:最低 256MB,推荐 512MB 以上
- 硬盘:10GB 以上可用空间(含数据库和备份)
- 已安装 Docker 和 Docker Compose
- 一个指向 VPS IP 的域名(如 vault.example.com)
安装 Docker 和 Docker Compose
如果你的 VPS 还没有安装 Docker,一行命令搞定:
1
2 curl -fsSL https://get.docker.com | sh
systemctl enable docker && systemctl start docker
安装完成后,验证版本:
1
2 docker --version
docker compose version
创建项目目录与配置文件
建一个专门的目录来存放 Vaultwarden 的所有配置和数据:
1
2 mkdir -p /opt/vaultwarden/data
cd /opt/vaultwarden
创建
1 | .env |
环境变量文件,这是最核心的配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 # /opt/vaultwarden/.env
# ===== 基础配置 =====
DOMAIN=https://vault.example.com
# ===== 数据库配置 =====
# 默认使用 SQLite,数据文件位于 data/db.sqlite3
# 如需 MySQL,取消注释以下行:
# DATABASE_URL=mysql://vaultwarden:password@db:3306/vaultwarden
# ===== 安全配置 =====
# 管理面板令牌(强烈建议设置)
ADMIN_TOKEN=YourRandomLongTokenHere
# 邀请令牌(防止陌生人注册)
INVITATIONS_ALLOWED=false
# 注册开放(生产环境务必关闭!)
SIGNUPS_ALLOWED=false
# 邮箱验证
SIGNUPS_VERIFY=true
# ===== 安全强化 =====
# 登录尝试限制
LOGIN_RATELIMIT_MAX_BURST=3
LOGIN_RATELIMIT_SECONDS=60
# ===== SMTP 邮件配置 =====
SMTP_HOST=smtp.gmail.com
SMTP_FROM=vault@example.com
SMTP_PORT=587
SMTP_SECURITY=starttls
SMTP_USERNAME=you@gmail.com
SMTP_PASSWORD=your-app-password
# ===== 其他配置 =====
# WebSocket 通知(用于实时同步)
WEBSOCKET_ENABLED=true
# 日志级别
LOG_LEVEL=info
# 图标下载服务
ICON_SERVICE=internal
注意:
1 | ADMIN_TOKEN |
请务必使用一个足够长且随机的字符串,这相当于你密码管理器的后门钥匙。可以用以下命令生成:
1 openssl rand -base64 48
编写 Docker Compose 文件
创建
1 | docker-compose.yml |
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 # /opt/vaultwarden/docker-compose.yml
version: "3.8"
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: always
ports:
- "127.0.0.1:8080:80" # HTTP 端口,仅监听本地
- "127.0.0.1:3012:3012" # WebSocket 端口,仅监听本地
volumes:
- ./data:/data
env_file:
- .env
environment:
- WEBSOCKET_ENABLED=true
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80/alive"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
一个关键细节:端口映射使用了
1 | 127.0.0.1 |
前缀,确保 Vaultwarden 只监听本地回环地址,不直接暴露到公网。所有外部流量必须通过 Nginx 反代,这是第一层安全屏障。
启动服务:
1
2
3
4
5
6 cd /opt/vaultwarden
docker compose up -d
# 检查状态
docker compose ps
docker compose logs --tail=20
看到
1 | vaultwarden |
容器状态为
1 | Up |
且健康检查通过,说明服务已正常启动。
Nginx 反向代理与 HTTPS 证书配置
密码管理器绝对不能在 HTTP 明文下运行,HTTPS 是硬性要求。这里我们用 Nginx 做反向代理,配合 Let’s Encrypt 自动签发证书。
安装 Nginx 和 Certbot
1 apt update && apt install -y nginx certbot python3-certbot-nginx
创建 Nginx 配置
创建
1 | /etc/nginx/sites-available/vaultwarden.conf |
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78 # /etc/nginx/sites-available/vaultwarden.conf
server {
listen 80;
server_name vault.example.com;
# Certbot 验证路径
location /.well-known/acme-challenge/ {
root /var/www/html;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
server_name vault.example.com;
# SSL 证书路径
ssl_certificate /etc/letsencrypt/live/vault.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/vault.example.com/privkey.pem;
# SSL 安全配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# HSTS(强制 HTTPS)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# 安全头部
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options SAMEORIGIN;
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy no-referrer;
# 日志
access_log /var/log/nginx/vaultwarden.access.log;
error_log /var/log/nginx/vaultwarden.error.log;
# 客户端最大上传(附件)
client_max_body_size 525M;
# Vaultwarden Web 界面和 API
location / {
proxy_pass http://127.0.0.1:8080;
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;
}
# WebSocket 实时通知
location /notifications/hub {
proxy_pass http://127.0.0.1:3012;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header 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;
}
# 管理面板(建议限制访问 IP)
location /admin {
# allow 你的IP地址;
# deny all;
proxy_pass http://127.0.0.1:8080;
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;
}
}
启用配置并获取证书:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 # 创建软链接启用站点
ln -s /etc/nginx/sites-available/vaultwarden.conf /etc/nginx/sites-enabled/
# 测试 Nginx 配置
nginx -t
# 先用 HTTP 配置启动 Nginx
systemctl reload nginx
# 申请 Let's Encrypt 证书
certbot --nginx -d vault.example.com --non-interactive --agree-tos -m your@email.com
# 重新加载 Nginx
systemctl reload nginx
现在打开浏览器访问
1 | https://vault.example.com |
,你应该能看到 Bitwarden 的登录页面了。
首次配置与账号创建
服务跑起来后,第一件事是创建你的管理员账号。由于我们在
1 | .env |
中设置了
1 | SIGNUPS_ALLOWED=false |
,外部用户无法自行注册。你需要通过管理面板来创建账号:
- 访问
1https://vault.example.com/admin
- 输入你在
1ADMIN_TOKEN
中设置的令牌
- 在管理面板中找到「Users」区域,点击「Invite User」
- 输入你的邮箱地址,发送邀请
- 查看邮箱,点击邀请链接设置主密码
创建完成后,建议在管理面板中将
1 | SIGNUPS_ALLOWED |
确认为
1 | false |
,并将自己的账号设为管理员。这样即使有人知道了你的域名,也无法注册。
关于主密码的选择——这是你所有密码的总钥匙,务必做到:
- 长度至少 16 个字符
- 包含大小写字母、数字和特殊符号
- 不要使用字典中的单词或个人信息
- 建议使用 DiceWare 方式生成的密码短语
生产环境安全加固
密码管理器是安全敏感型应用,部署完只是第一步,加固才是关键。以下是我建议的安全措施,按优先级排列:
1. 防火墙配置
只开放必要的端口,其余全部关闭:
1
2
3
4
5
6
7
8 # 使用 ufw
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp # SSH
ufw allow 80/tcp # HTTP(证书续期)
ufw allow 443/tcp # HTTPS
ufw enable
ufw status
确保 Docker 的 8080 和 3012 端口没有暴露到公网(我们已经在 docker-compose.yml 中绑定了 127.0.0.1)。
2. Fail2ban 防暴力破解
1 apt install -y fail2ban
创建
1 | /etc/fail2ban/filter.d/vaultwarden.conf |
:
1
2
3 [Definition]
failregex = ^.*Username or password is incorrect\. Try again\. IP: <HOST>\. Username:.*$
ignoreregex =
创建
1 | /etc/fail2ban/jail.d/vaultwarden.local |
:
1
2
3
4
5
6
7
8
9 [vaultwarden]
enabled = true
port = 80,443
filter = vaultwarden
action = iptables-allports[name=vaultwarden]
logpath = /var/log/nginx/vaultwarden.access.log
maxretry = 3
bantime = 3600
findtime = 600
启动 Fail2ban:
1
2
3 systemctl enable fail2ban
systemctl restart fail2ban
fail2ban-client status vaultwarden
3. 管理面板 IP 白名单
在 Nginx 配置中,取消
1 | /admin |
location 块里的 IP 限制注释,只允许你自己的 IP 访问:
1
2
3
4
5 location /admin {
allow 你的公网IP;
deny all;
# ... 其他 proxy 配置
}
如果你是动态 IP,可以考虑只在使用时临时开放,或者通过 VPN 连接后再访问。
4. 启用两步验证(2FA)
登录 Vaultwarden 网页端后,进入「设置」→「两步登录」,建议至少启用以下之一:
- TOTP 验证器(推荐):使用 Authy、Google Authenticator 等应用扫码绑定
- WebAuthn / FIDO2(最安全):使用 YubiKey 等硬件安全密钥
- Duo:通过 Duo Security 推送通知验证
5. 安全头部与 CSP 强化
在 Nginx 配置中添加 Content-Security-Policy 头,限制资源加载来源:
1 add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://haveibeenpwned.com; connect-src 'self' https://api.pwnedpasswords.com https://2fa.directory; frame-ancestors 'none';" always;
这个 CSP 策略允许 Vaultwarden 正常工作(包括密码泄露检查功能),同时阻止外部脚本的加载。
数据备份与灾难恢复
密码管理器的数据比 VPS 本身更重要——VPS 挂了可以重装,密码丢了就真的丢了。必须建立可靠的备份机制。
本地自动备份脚本
创建
1 | /opt/vaultwarden/backup.sh |
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 #!/bin/bash
# Vaultwarden 自动备份脚本
BACKUP_DIR="/opt/vaultwarden/backups"
DATA_DIR="/opt/vaultwarden/data"
DATE=$(date +%Y%m%d_%H%M%S)
RETAIN_DAYS=30
mkdir -p $BACKUP_DIR
# SQLite 数据库安全备份
if command -v sqlite3 > /dev/null; then
sqlite3 "$DATA_DIR/db.sqlite3" ".backup '$BACKUP_DIR/db_$DATE.sqlite3'"
else
echo "WARN: sqlite3 not found, stopping container for backup..."
cd /opt/vaultwarden && docker compose stop
cp "$DATA_DIR/db.sqlite3" "$BACKUP_DIR/db_$DATE.sqlite3"
cd /opt/vaultwarden && docker compose start
fi
# 备份附件目录
tar czf "$BACKUP_DIR/attachments_$DATE.tar.gz" -C "$DATA_DIR" attachments/ 2>/dev/null || true
# 备份 RSA 密钥(用于两步验证)
tar czf "$BACKUP_DIR/rsa_keys_$DATE.tar.gz" -C "$DATA_DIR" rsa_key* 2>/dev/null || true
# 备份配置文件
cp /opt/vaultwarden/.env "$BACKUP_DIR/env_$DATE.bak"
cp /opt/vaultwarden/docker-compose.yml "$BACKUP_DIR/docker-compose_$DATE.bak"
# 合并为一个完整备份包
tar czf "$BACKUP_DIR/vaultwarden_full_$DATE.tar.gz" -C "$BACKUP_DIR" "db_$DATE.sqlite3" "attachments_$DATE.tar.gz" "rsa_keys_$DATE.tar.gz" "env_$DATE.bak" "docker-compose_$DATE.bak"
# 清理临时文件
rm -f "$BACKUP_DIR/db_$DATE.sqlite3" "$BACKUP_DIR/attachments_$DATE.tar.gz" "$BACKUP_DIR/rsa_keys_$DATE.tar.gz" "$BACKUP_DIR/env_$DATE.bak" "$BACKUP_DIR/docker-compose_$DATE.bak"
# 删除超过保留天数的旧备份
find $BACKUP_DIR -name "vaultwarden_full_*.tar.gz" -mtime +$RETAIN_DAYS -delete
echo "[$DATE] Backup completed: vaultwarden_full_$DATE.tar.gz"
1 chmod +x /opt/vaultwarden/backup.sh
定时任务与异地备份
设置 cron 每天凌晨 3 点自动备份:
1
2
3
4
5 # 编辑 crontab
crontab -e
# 添加以下行
0 3 * * * /opt/vaultwarden/backup.sh >> /opt/vaultwarden/backups/backup.log 2>&1
本地备份只是第一步,更重要的是异地备份。你可以使用 rclone 将备份同步到云存储:
1
2
3
4
5
6
7
8 # 安装 rclone
curl https://rclone.org/install.sh | sudo bash
# 配置远程存储
rclone config
# 在 backup.sh 末尾添加异地同步
rclone copy /opt/vaultwarden/backups/ remote:vaultwarden-backup/ --max-age 30d
灾难恢复流程
当需要恢复时,按以下步骤操作:
- 在新 VPS 上安装 Docker 和 Docker Compose
- 创建
1/opt/vaultwarden
目录
- 从备份中恢复
1.env
和
1docker-compose.yml - 解压数据库和附件到
1data/
目录
-
1docker compose up -d
启动服务
- 配置 Nginx 和 HTTPS
整个恢复过程可以在 15 分钟内完成,前提是备份文件齐全。
客户端配置与日常使用
Vaultwarden 最大的优势是完全兼容 Bitwarden 官方客户端。你不需要安装任何第三方应用,直接使用 Bitwarden 官方客户端即可。
浏览器扩展
Chrome、Firefox、Edge、Safari 都有官方 Bitwarden 扩展。安装后,点击「设置」→「自托管」,填入你的服务器地址
1 | https://vault.example.com |
,然后正常登录即可。
手机客户端
iOS 和 Android 都有官方 Bitwarden App。同样在设置中将服务器改为自托管地址。注意:某些旧版本的 Android 客户端可能需要在「高级设置」中单独填写服务器 URL。
桌面客户端
Windows、macOS、Linux 均有官方桌面客户端。配置方式与浏览器扩展相同。
CLI 命令行工具
对于喜欢命令行的用户,Bitwarden 还提供了 CLI 工具:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 # 安装
npm install -g @bitwarden/cli
# 登录(指定自托管服务器)
bw config server https://vault.example.com
bw login you@example.com
# 解锁保险库
export BW_SESSION=$(bw unlock --raw)
# 常用操作
bw list items
bw get item "GitHub"
bw sync
CLI 工具在自动化场景中特别有用——比如从 CI/CD 流水线中读取部署密码,或者在服务器间同步配置。
高级功能与技巧
组织功能(团队共享)
Vaultwarden 支持组织(Organization)功能,可以和家人或小团队成员共享密码。创建组织后,可以建立不同权限的集合(Collection),实现只读共享、按组分配等精细化权限管理。这对于小团队管理共享账号(如社交媒体、云服务控制台等)非常实用。
密码泄露检测
Vaultwarden 集成了 Have I Been Pwned 的密码泄露检测功能。当你保存新密码或在安全检查时,它会将密码的 SHA-1 哈希前 5 位发送到 HIBP API(k-匿名模型,不会泄露完整密码),检查该密码是否出现在已知的泄露数据库中。如果匹配到,客户端会发出警告。
紧急访问
这是一个被低估但非常重要的功能:你可以指定信任的紧急联系人,在你无法访问密码库时(比如失联、意外),他们可以在等待期过后获得访问权限。设置方式是进入「设置」→「紧急访问」,添加联系人并设置等待天数(建议 7-14 天)。
API 集成与自动化
Vaultwarden 完全兼容 Bitwarden 的 REST API,你可以编写脚本自动化密码管理。比如定期检查并轮换服务器密码、从 CI/CD 流水线读取部署密钥、在服务器初始化时自动注入凭证等。配合 Bitwarden CLI 工具,这些操作都可以脚本化完成。
常见问题与故障排除
WebSocket 连接失败
症状:客户端无法实时同步,需要手动刷新。检查 Nginx 配置中
1 | /notifications/hub |
的 WebSocket 代理是否正确,确保包含了
1 | Upgrade |
和
1 | Connection |
头。同时确认
1 | .env |
中
1 | WEBSOCKET_ENABLED=true |
。
附件上传失败
检查 Nginx 配置中的
1 | client_max_body_size |
,默认值很小,建议设为
1 | 525M |
。同时检查 VPS 磁盘空间是否充足。
邮件发送失败
检查 SMTP 配置是否正确,尤其是 Gmail 用户需要使用「应用专用密码」而非账户密码。查看日志:
1 | docker compose logs | grep smtp |
。
证书续期失败
Certbot 续期时需要通过 80 端口验证域名。确保防火墙放行了 80 端口,且 Nginx 正在运行。手动续期测试:
1 | certbot renew --dry-run |
。
容器更新
1
2
3
4
5
6 cd /opt/vaultwarden
docker compose pull
docker compose up -d
# 更新后检查
docker compose logs --tail=20
Vaultwarden 更新非常频繁,建议每月检查一次。更新不会影响数据,但重大版本升级前建议手动执行一次备份。
性能优化与监控
对于大多数个人用户来说,Vaultwarden 的默认配置已经足够。但如果你有更高的要求,这里有几个优化方向:
数据库优化
如果使用 SQLite,可以定期执行 VACUUM 来优化数据库文件大小:
1
2 # 在备份脚本中添加
sqlite3 /opt/vaultwarden/data/db.sqlite3 "VACUUM;"
对于用户量较大的场景,建议切换到 MariaDB/PostgreSQL。修改
1 | .env |
中的
1 | DATABASE_URL |
并启动对应的数据库容器即可。
图标缓存
Vaultwarden 默认会为每个密码条目下载网站图标(favicon),这会产生大量外部请求。如果你不介意图标缺失,可以设置
1 | ICON_SERVICE=internal |
来跳过外部图标下载,减少网络开销。
健康检查与监控
Vaultwarden 提供了
1 | /alive |
端点用于健康检查。你可以用简单的脚本监控服务状态:
1
2
3
4
5
6
7
8
9
10
11 #!/bin/bash
# 简单的 Vaultwarden 健康监控
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" https://vault.example.com/alive)
if [ "$RESPONSE" != "200" ]; then
echo "Vaultwarden is DOWN! HTTP: $RESPONSE"
# 发送通知(Telegram/邮件等)
# 重启服务
cd /opt/vaultwarden && docker compose restart
fi
结合 Uptime Kuma 或 Prometheus + Grafana 监控体系,可以做到服务异常时自动告警。
总结
自建密码管理器并不复杂,一台 512MB 的小 VPS 就能跑起来,但安全责任完全在你自己身上。回顾一下关键要点:
- 必须启用 HTTPS——没有 HTTPS 的密码管理器等于裸奔
- 关闭公开注册——只允许通过管理员邀请创建账号
- 设置强管理令牌——ADMIN_TOKEN 越长越好,定期更换
- 启用两步验证——这是防止主密码泄露后的最后一道防线
- 做好异地备份——本地备份+云存储双保险,定期验证恢复流程
- 限制管理面板访问——Nginx 层面 IP 白名单
- 保持更新——Vaultwarden 更新频繁,及时跟进安全补丁
把密码管理权掌握在自己手里,而不是交给第三方——这就是自托管的魅力,也是 VPS 玩家应该具备的基本功。如果你还没有开始使用密码管理器,现在就是最好的时机。
汤不热吧