
1. 为什么要在自己的服务器上搭一套密码管理服务密码这件事很多人嘴上说重视实际做法却是在浏览器里点“记住密码”或者干脆所有平台共用一个。等到某个小网站被拖库撞库攻击一上来邮箱、社交、支付全线告急。商业密码管理服务确实省事但把全部身家性命托付给第三方总有人心里不踏实——尤其是那些手里管着服务器、数据库、云账号的运维和开发者密码一旦泄露损失不是换个账号那么简单。自己搭一套密码管理服务核心诉求就三个数据完全掌握在自己手里、多端同步不折腾、加密强度经得起推敲。Bitwarden 正好踩在这三个点上。它是开源项目客户端覆盖浏览器插件、桌面端、手机端服务端可以自托管加密模型是端到端设计——主密码不出本地服务器上存的都是密文。换句话说就算服务器被拖走攻击者拿到的也只是一堆解不开的乱码。这篇内容面向的是手里有一台闲置服务器、懂一点 Linux 基础操作、想把密码管理这件事彻底收归自有的人。CentOS 7 虽然已经进入维护尾声但大量存量服务器还在跑它所以这套搭建流程依然有现实意义。我会从方案选型讲到具体落地把每一步为什么这么做、参数怎么定、坑在哪里都掰开说清楚。整套流程走下来一台 2 核 4G 的入门级云主机就够用成本可控维护量也不大。需要提前说明的是下面涉及的具体 IP、域名、密码全部是示例占位你实际操作时替换成自己的即可。整套方案基于常见的自托管实践补全Bitwarden 官方也提供了对应的部署脚本和文档思路是一致的。2. 方案选型与整体架构拆解2.1 为什么选 Bitwarden 自托管而不是别的方案市面上的密码管理方案大致分三类纯本地离线工具、商业云服务、开源自托管。纯本地工具的问题是同步麻烦换台设备就得手动导来导去商业云服务省心但数据不在自己手里开源自托管是折中路线兼顾掌控力和便利性。Bitwarden 在开源自托管这一档里优势明显。第一客户端生态完整Chrome、Firefox、Edge 插件齐全iOS 和 Android 都有官方 App桌面端也有日常使用体验和商业产品差距不大。第二加密模型清晰主密码经过 PBKDF2 或 Argon2 派生后只在本地参与加解密服务器端拿到的是已经加密的保险库数据。第三社区活跃部署脚本成熟出问题容易找到参考。对比其他自托管方案有的项目只提供命令行客户端有的同步机制依赖第三方存储有的加密实现不够透明。Bitwarden 的官方安装脚本把 Docker、数据库、反向代理这些组件串起来了对有一定 Linux 基础的人来说上手门槛不算高。2.2 整体架构长什么样整套服务的骨架可以拆成四层。最底层是操作系统CentOS 7 提供基础运行环境。往上是容器运行时Docker 负责把 Bitwarden 的各个服务组件隔离运行。再往上是应用层包括 Web 保险库、API 接口、身份认证、数据库等。最外层是入口层Nginx 做反向代理负责把外部 HTTPS 请求转发到内部服务同时处理证书和域名。数据流向是这样的用户在浏览器或手机 App 里输入主密码客户端本地完成密钥派生和加解密只把密文通过 HTTPS 发给服务器服务器收到密文后存进数据库返回时也是密文客户端拿到密文再本地解密展示。整个过程中明文密码和主密码都不会离开用户设备。这个设计是自托管方案安全性的根基理解这一点后面配置 HTTPS 和数据库时就知道哪些环节不能马虎。2.3 硬件与系统的最低要求Bitwarden 官方对自托管的最低配置建议是 2 核 CPU、4GB 内存、至少 20GB 磁盘。实测下来个人或小团队使用十人以内这个配置跑起来比较从容。如果只有 1 核 2G安装过程可能因为内存不足导致容器启动失败尤其是数据库初始化阶段比较吃内存。CentOS 7 方面建议用最小化安装减少不必要的服务暴露面。系统盘 20G 起步因为 Docker 镜像和数据库会占空间后续备份也需要余量。网络方面需要一个公网 IP 和一个能解析到该 IP 的域名HTTPS 证书申请依赖域名验证没有域名的话只能用自签证书客户端信任会比较麻烦。提示CentOS 7 的默认内核版本较老Docker 安装时要注意选择兼容的版本后面会具体说。3. 系统准备与基础环境配置3.1 系统更新与基础工具安装拿到一台干净的 CentOS 7 之后第一件事是更新系统并装好常用工具。这一步看似简单但顺序有讲究——先更新再装软件避免依赖版本冲突。yum update -y yum install -y yum-utils device-mapper-persistent-data lvm2 git curl wget vimyum-utils提供yum-config-manager后面添加 Docker 仓库要用device-mapper-persistent-data和lvm2是 Docker 存储驱动的基础依赖缺了它们 Docker 跑不起来。这几个包看着不起眼但漏装会导致后续步骤卡住我见过有人直接跳过更新去装 Docker结果因为旧版依赖冲突折腾半天。更新完成后建议重启一次让内核和库文件生效。重启后用uname -r确认内核版本CentOS 7 一般是 3.10 系列够用。3.2 关闭 SELinux 与防火墙的取舍CentOS 7 默认开启 SELinux 和 firewalld这两个东西在正式生产环境里是安全加固手段但在搭建自托管服务的初期它们经常成为“服务起不来”的元凶。SELinux 会限制容器访问宿主机文件firewalld 会拦截 Docker 映射的端口。我的做法是搭建阶段先把 SELinux 设为 permissive 模式firewalld 暂时停用等服务跑通、端口确认无误后再按需收紧。这样能把“环境问题”和“配置问题”分开排查效率高很多。setenforce 0 sed -i s/^SELINUXenforcing/SELINUXpermissive/ /etc/selinux/config systemctl stop firewalld systemctl disable firewalldsetenforce 0是临时生效改配置文件是永久生效两条都要做。防火墙方面如果这台机器只跑 Bitwarden后续用云厂商的安全组来控制入站端口更省事宿主机层面的 firewalld 可以保持关闭。但如果你对安全要求高也可以在服务跑通后重新启用 firewalld只放行 80、443 和 SSH 端口。注意SELinux 设为 permissive 只是记录违规不拦截不是彻底关闭。如果后续遇到权限相关的诡异问题可以临时用setenforce 0对比测试确认是不是 SELinux 在作祟。3.3 时间同步与主机名设置时间同步这件事容易被忽略但密码服务对时间敏感——TLS 证书验证、令牌有效期都依赖准确的时间。CentOS 7 自带 chronyd确认它正常运行即可。systemctl enable chronyd systemctl start chronyd timedatectl set-timezone Asia/Shanghai chronyc sources -v时区设成自己所在区域chronyc sources能看到同步源状态就说明正常。主机名方面设一个好记的名字后续证书和配置里会用到。hostnamectl set-hostname vault主机名不影响功能但日志和监控里看着清楚排查问题时省心。4. Docker 环境搭建与镜像加速4.1 安装 Docker CE 的正确姿势CentOS 7 自带的docker包版本太老必须用 Docker 官方仓库装 CE 版本。添加仓库时要注意CentOS 7 已经停止维护部分镜像源可能失效建议用国内可访问的镜像源。yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo yum install -y docker-ce docker-ce-cli containerd.io装完后启动并设为开机自启systemctl enable docker systemctl start docker docker versiondocker version能同时看到 Client 和 Server 信息说明 Docker 守护进程正常。如果只显示 Client 部分说明服务没起来用systemctl status docker看日志。4.2 配置镜像加速与日志限制拉取 Bitwarden 镜像时默认从 Docker Hub 拉速度可能很慢甚至超时。配置一个镜像加速地址能显著改善体验。编辑/etc/docker/daemon.json{ registry-mirrors: [https://your-mirror.example.com], log-driver: json-file, log-opts: { max-size: 50m, max-file: 3 } }registry-mirrors填你可用的加速地址这里用示例占位。日志限制这两项很关键——Bitwarden 的容器日志如果不限制几个月就能把磁盘写满我遇到过磁盘被日志撑爆导致数据库写入失败的情况。max-size设 50MB、保留 3 个文件单个容器日志上限 150MB够用且安全。改完配置重启 Dockersystemctl daemon-reload systemctl restart docker4.3 验证 Docker 网络与存储Bitwarden 安装脚本会创建自定义网络容器之间通过服务名通信。确认 Docker 的默认网桥正常docker network ls docker info | grep -i storage存储驱动一般是 overlay2CentOS 7 内核 3.10 支持。如果显示的是 devicemapper性能会差一些建议检查是否漏装了lvm2相关包。网络方面默认的 bridge 网络存在即可安装脚本会自己建新网络。5. Bitwarden 服务端部署实操5.1 获取安装脚本与目录规划Bitwarden 官方提供了一个bitwarden.sh脚本负责下载配置、生成密钥、启动容器。先建一个专用目录把脚本放进去mkdir -p /opt/bitwarden cd /opt/bitwarden curl -Lso bitwarden.sh https://raw.githubusercontent.com/bitwarden/server/master/scripts/bitwarden.sh chmod x bitwarden.sh目录选/opt是惯例数据、配置、备份都集中在这里后续迁移或备份时打包整个目录就行。脚本下载地址如果访问不畅可以换用镜像源或者手动下载后上传思路一样。5.2 安装命令与交互式配置详解执行安装命令./bitwarden.sh install脚本会依次询问几个关键参数每一个都影响后续运行逐个说清楚Enter the domain name填你的域名比如vault.example.com。这个域名必须已经解析到服务器公网 IP后面申请证书要用。填错的话HTTPS 会失败客户端连不上。Do you want to use a self-signed certificate?没有正式证书就选yes但客户端会提示证书不受信任。建议选no让脚本用 Lets Encrypt 自动申请前提是域名解析正确且 80 端口可达。Enter the email address填一个真实可用的邮箱证书到期提醒和紧急通知会发到这里。Enter your installation id / key这两个参数需要从 Bitwarden 官方获取用于标识你的安装实例。获取方式在官方文档里有说明按提示操作即可。配置过程中生成的config.yml和global.override.env都在/opt/bitwarden/bwdata下前者管域名和端口后者管数据库密码、SMTP 等敏感配置。这两个文件要妥善保管尤其是global.override.env里有数据库密码。5.3 启动服务与状态检查安装完成后启动./bitwarden.sh start首次启动会拉取多个镜像耗时取决于网络。启动完成后用以下命令检查容器状态./bitwarden.sh status docker ps正常情况下能看到bitwarden-nginx、bitwarden-api、bitwarden-web、bitwarden-identity、bitwarden-db等容器在运行。如果某个容器反复重启用docker logs 容器名看日志常见原因是数据库还没初始化完、内存不足、端口冲突。服务起来后浏览器访问https://vault.example.com能看到登录页面就说明部署成功。首次使用需要注册账号注册时设置的主密码就是你的保险库钥匙务必牢记——这个密码丢了数据就真的解不开了官方也帮不了你。提示注册账号后建议立刻在设置里开启两步验证多一层保护。自托管实例的两步验证支持 TOTP 应用配置起来不复杂。6. 反向代理、HTTPS 与访问优化6.1 Nginx 反向代理的角色Bitwarden 安装脚本自带了一个 Nginx 容器做入口但很多人习惯在宿主机再套一层 Nginx原因有几个统一管理多个站点的证书、方便加访问日志和限流、宿主机 Nginx 处理静态资源更灵活。如果你只跑 Bitwarden 一个服务用自带的 Nginx 就够不必多套一层。如果确实需要宿主机 Nginx配置大致如下server { listen 443 ssl http2; 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; 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; } }proxy_set_header这几行不能省Bitwarden 靠这些头判断原始请求的协议和来源缺了会导致重定向循环或 WebSocket 连接失败。proxy_pass指向 Bitwarden 自带 Nginx 映射的端口默认是 8080具体看config.yml里的配置。6.2 证书申请与自动续期用 Lets Encrypt 申请证书certbot 是最省事的工具。CentOS 7 上装 certbotyum install -y certbot python2-certbot-nginx certbot --nginx -d vault.example.com按提示操作certbot 会自动改 Nginx 配置并申请证书。续期用certbot renew --dry-run测试确认无误后加一个定时任务echo 0 3 * * * /usr/bin/certbot renew --quiet | crontab -每天凌晨 3 点检查续期证书到期前 30 天会自动更新。这个定时任务别忘了加我见过证书过期导致客户端集体连不上的事故排查起来很浪费时间。6.3 访问速度与安全加固自托管服务放在公网访问速度受服务器带宽影响。如果服务器在境外国内访问可能偏慢可以考虑在客户端设置里调整同步频率减少不必要的请求。安全方面几个基础加固动作值得做修改 SSH 端口禁用密码登录改用密钥认证。云厂商安全组只放行 80、443 和自定义 SSH 端口。Bitwarden 管理后台/admin设置强密码这个入口能改配置权限很高。定期检查docker ps和日志发现异常容器及时处理。这些动作不复杂但能挡掉大部分自动化扫描和暴力破解。7. 数据备份、恢复与日常维护7.1 备份哪些东西才算完整自托管服务最怕的是数据丢失。Bitwarden 的数据分两块数据库里的密文保险库和bwdata目录下的配置文件。备份必须两者都覆盖只备数据库不备配置恢复时域名、密钥对不上服务照样起不来。完整的备份对象包括备份对象路径说明数据库bwdata/mssql/data或通过脚本导出核心数据必须备配置文件bwdata/config.yml、bwdata/global.override.env含密钥和密码必须备证书bwdata/ssl或宿主机证书目录可选重新申请也行附件bwdata/attachments如有用户上传的附件Bitwarden 脚本自带备份命令./bitwarden.sh backup备份文件默认放在bwdata/backups下。建议再加一层异地备份用rsync或对象存储同步到另一台机器避免单点故障。7.2 恢复流程与验证恢复时先停服务把备份文件放回对应位置再启动./bitwarden.sh stop # 恢复数据库和配置文件 ./bitwarden.sh start恢复后务必验证登录 Web 端检查保险库条目是否完整用客户端同步一次确认数据一致。我建议每季度做一次恢复演练把备份恢复到测试环境跑一遍确认备份真的可用——备份文件损坏或恢复步骤遗漏的情况并不少见等到真出事才发现就晚了。7.3 版本升级与回滚策略Bitwarden 更新比较频繁升级用脚本一条命令./bitwarden.sh update升级前先备份这是铁律。升级后检查容器状态和日志确认没有报错。如果新版本有问题回滚的方式是恢复升级前的备份所以备份文件要保留至少两个版本。注意跨大版本升级时官方有时会要求先升级到中间版本直接跳版可能失败。升级前看一眼官方发布说明确认有没有特殊要求。8. 常见问题排查与避坑经验8.1 容器起不来怎么查容器反复重启是最常见的问题。排查顺序先看docker logs再看资源占用最后看端口冲突。现象可能原因排查方法数据库容器退出内存不足free -h看可用内存4G 以下容易出问题API 容器重启数据库未就绪等几分钟再docker ps或看 API 日志里的连接错误Nginx 容器退出端口被占用ss -tlnp看 80/443 是否被其他进程占用全部容器正常但访问 502反向代理配置错误检查proxy_pass端口和请求头内存不足是最隐蔽的坑。CentOS 7 本身占几百兆Docker 和几个容器加起来2G 内存很容易在数据库初始化时 OOM。解决办法是加 swapdd if/dev/zero of/swapfile bs1M count2048 chmod 600 /swapfile mkswap /swapfile swapon /swapfile echo /swapfile swap swap defaults 0 0 /etc/fstab2G swap 能缓解内存压力但治标不治本长期用还是建议升到 4G 内存。8.2 HTTPS 证书相关的坑证书问题集中在两个场景申请失败和客户端不信任。申请失败多半是域名解析没生效或 80 端口不通用dig和curl验证一下。客户端不信任常见于自签证书浏览器和手机 App 都会拦截解决办法是换正式证书或者把自签证书导入客户端信任链操作繁琐不推荐。还有一个坑是证书续期后服务没重载。certbot 续期只更新文件Nginx 需要 reload 才生效。在续期命令后加--deploy-hook systemctl reload nginx可以自动重载。8.3 客户端同步失败的排查思路客户端连不上服务端先确认服务端本身可访问浏览器能打开 Web 端说明 HTTPS 和反向代理没问题。然后检查客户端配置的服务器地址是否正确有没有多写或漏写https://。再检查客户端版本过旧的客户端可能不兼容新版服务端 API。如果 Web 端正常但客户端同步失败看服务端日志里有没有对应请求记录。没有记录说明请求没到服务端问题在网络或客户端配置有记录但报错看错误码定位。常见的是 401认证失败和 502后端不可用前者检查账号密码后者检查容器状态。8.4 磁盘空间与日志管理磁盘写满是自托管服务的慢性病。除了前面说的 Docker 日志限制还要关注数据库日志和备份文件堆积。定期用df -h和du -sh /opt/bitwarden/bwdata/*检查占用备份文件保留最近几个即可旧的清理掉。数据库的mssql日志增长较快可以在管理后台调整日志级别或者定期执行数据库维护。这块不用天天盯但每月看一眼能避免突然写满导致服务中断。8.5 我踩过的几个真实坑第一个坑是安装时域名填错后面改config.yml重新生成配置结果证书和配置对不上折腾了半天才理清。教训是安装前把域名解析确认好一次填对。第二个坑是没限制 Docker 日志跑了三个月磁盘满了数据库写入失败客户端同步全部报错。加了日志限制后这个问题再没出现过。第三个坑是升级前没备份新版本有个配置项变更导致服务起不来只能重装。从那以后升级前必备份这个习惯救了我好几次。这些坑都不复杂但都是实际会遇到的。自托管服务的维护成本主要就花在这类细节上提前知道能省不少时间。9. 使用体验与后续扩展方向服务跑起来之后日常使用体验和商业产品差别不大。浏览器插件登录一次后续自动填充手机 App 同步及时桌面端也能用。主密码建议用一句好记但足够长的话配合两步验证安全性有保障。后续如果想扩展几个方向可以考虑接入 SMTP 邮件服务让密码重置和通知能正常发信配置多个用户和组织适合小团队共享部分密码把备份自动化用脚本定时导出并同步到异地。这些扩展都不难按需逐步加就行。我个人在实际操作中的体会是自托管密码服务的价值不在于省了多少钱而在于数据归属清晰、心里踏实。维护上花的时间一个月也就检查一两次换来的是对自己数字身份的真正掌控。如果你手里有闲置服务器又对密码安全有要求这套方案值得一试。