别再给1Password交钱了!自建密码库保姆级教程:零订阅费,不用公网IP,手机电脑浏览器全同步
文章目录
密码管理器也要年年续费?自己搭一个密码库,不用公网IP,不用开端口,手机、电脑、浏览器全都能同步。
1Password这类密码管理器,好用是好用,就是每年都得续费。 我现在用的是自建的Vaultwarden,客户端直接用官方的Bitwarden App和浏览器插件,平时该怎么用还是怎么用。
Bitwarden官方免费版其实也能同步,但密码库存在别人的服务器上。自建的话,整个库都在自己机器上,备份、迁移都自己说了算。当然,如果不想折腾服务器,直接用官方的也挺好。
需要的只有一台常开的机器和一个域名,VPS、NAS、家里的旧电脑都行。我找了台闲置的云服务器从零重装了一遍,每一步都截了图,跟着做完你会得到:
-
一个用自己域名访问的密码库,iPhone、安卓、Mac、Windows、浏览器插件全都能用;
-
1Password、iCloud钥匙串、Chrome里存的密码,都能直接导进来;
-
几种常见的备份方案,机器挂了也能原样找回来。
每日叠甲:这是我自己的用法,不一定是最优解。有更好的玩法,欢迎在评论区补充。
整体结构
前面说的不用公网IP、不用开端口,靠的都是Cloudflare Tunnel。 Tunnel是服务器主动连出去的,所以家里宽带只有内网地址也没关系,服务器也不用对外开任何端口,扫端口的人根本找不到你的密码库。
顺带还有个好处:HTTPS证书Cloudflare自动给,不用自己折腾Nginx和Let's Encrypt。
准备工作
-
一台常开的机器:Vaultwarden本身只占几十MB内存,配置再低的机器都带得动。
-
装好Docker和Docker Compose。
-
一个托管在Cloudflare的域名:这篇用vault-demo.suversal.com演示,配置时换成你自己的即可。
一、部署Vaultwarden
新建一个专用目录,写好docker-compose.yml:
mkdir -p ~/vaultwarden && cd ~/vaultwarden
nano docker-compose.yml
填写Vaultwarden这个服务:
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
environment:
DOMAIN: "https://vault-demo.suversal.com"
SIGNUPS_ALLOWED: "true"
TZ: "Asia/Shanghai"
volumes:
- ./vw-data:/data
ports:
- "127.0.0.1:8081:80"
networks:
- tunnel
networks:
tunnel:
external: true
五个地方要注意:
1. DOMAIN一定要填,写成你最终访问的完整地址,带https://。不填的话,WebAuthn两步验证、附件下载这些功能会出问题。
2. SIGNUPS_ALLOWED先开着,等自己的账号注册完马上关掉,后面第三步会讲。
3. 端口写成127.0.0.1:8081:80,不要只写8081:80。
先说这串怎么读:服务器IP:服务器端口:容器端口。Vaultwarden在容器里监听的是80,映射到服务器的8081上。8081是我随便挑的,服务器上没被占用就行,换成别的也可以。
这一点很多教程没提:Docker映射的端口会绕过ufw和iptables的规则,你以为防火墙挡住了,其实整个公网都能直接访问。
加上127.0.0.1之后,这个端口只有本机能访问,外面进来只能走Tunnel。
4. ./vw-data:/data是数据存放的位置。
左边是服务器上的目录,也就是~/vaultwarden/vw-data,右边是容器里的路径。所有数据都写在这个目录里,容器删了重建也不会丢,后面备份的就是它。
5. networks是把它接到一个叫tunnel的共享网络上。
后面cloudflared会单独放一个目录,也接到这个网络上,这样它就能找到Vaultwarden,第二步会细讲。external: true的意思是这个网络不归这个compose管,要提前手动建好。
先建网络,再启动:
docker network create tunnel
docker compose up -d
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8081/
返回200就说明跑起来了。
看一眼数据目录:
$ ls vw-data
db.sqlite3 db.sqlite3-shm db.sqlite3-wal rsa_key.pem tmp
记住这几个文件,后面做备份的时候会用到:不是只有db.sqlite3一个文件。
到这一步,Vaultwarden服务其实已经跑起来了,只是暂时只有服务器自己能访问。
有朋友可能会想,直接把服务器安全组放行,用http://公网IP:8081访问不就完了?
不建议:一是网页版只在HTTPS下才能正常注册登录,二是密码库直接挂在公网端口上,谁都能扫到。所以下一步再花五分钟用Cloudflare套一层,自动有HTTPS,服务器也不用开任何端口。
二、用Cloudflare Tunnel把它安全地发布出去
1. 在Cloudflare后台创建隧道
Cloudflare后台最近改版了,Tunnels已经从Zero Trust挪到了联网→Tunnels。
点「创建隧道」,名字随便起:
2. 拿到Token
创建后,我们这里依旧用Docker部署,页面上会给一条docker run ... --token eyJ...的命令。
点右边的复制按钮,把--token后面那一长串单独存下来。这个Token等于这条隧道的钥匙,不要发给别人,也不要截图外传。
3. 给cloudflared单独建一个目录
我没有把cloudflared和Vaultwarden写在同一个compose里,而是单独放一个目录。
因为Tunnel不只是给密码库用的,以后家里的NAS、图床、导航页想发布出去,都可以走这同一条隧道。单独管理的话,改Vaultwarden、重建Vaultwarden都不会影响别的服务。
Cloudflare Tunnel真的是非常好用的服务,后续有机会再详细给大家介绍更多用法。
先把Token写进.env文件:
mkdir -p ~/cloudflared && cd ~/cloudflared
echo 'TUNNEL_TOKEN=粘贴你的Token' > .env
chmod 600 .env
docker compose启动时会自动读取同目录下的.env,compose文件里的${TUNNEL_TOKEN}就会被换成真正的Token。这样Token不用写进docker-compose.yml,这个文件截图、发给别人看都没关系。
然后新建~/cloudflared/docker-compose.yml:
services:
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: unless-stopped
command: tunnel --no-autoupdate run
environment:
TUNNEL_TOKEN: ${TUNNEL_TOKEN}
networks:
- tunnel
networks:
tunnel:
external: true
--no-autoupdate是不让cloudflared在容器里自己升级,要更新就重新拉镜像。
最后的networks和Vaultwarden那边一样,接到同一个tunnel网络上,这是两个目录能互相找到的关键。以后别的服务想走这条隧道,也是在它的compose里加上这几行。
启动并看日志:
docker compose up -d
docker logs cloudflared 2>&1 | grep Registered
看到4行Registered tunnel connection就是连上了(cloudflared会同时连4个Cloudflare节点,互为备份),回到网页也会显示「隧道连接成功」:
4. 添加路由
点「继续」回到列表,进入刚建的隧道→添加路由→已发布的应用程序,这样填:
-
子域名:vault-demo(换成你的)
-
域:选你的域名
-
Service URL:http://vaultwarden:80
这样就算服务器没有公网IP,也能通过Cloudflare Tunnel从外网访问到这个服务。
Service URL填的是http://vaultwarden:80,不是localhost:8081,这里最容易填错。
-
vaultwarden是什么:它不是公网域名,是Vaultwarden那个compose里给服务起的名字。两个容器都接在tunnel网络上,Docker会在这个网络里把服务名解析成对应容器的内网地址,所以cloudflared直接用这个名字就能找到Vaultwarden。
-
为什么不是localhost:虽然都在同一台服务器上,但每个容器都有自己独立的网络,相当于服务器里的一台小虚拟机。cloudflared在自己的容器里访问localhost,找的是它自己,不是服务器,自然连不上。
-
为什么是80不是8081:8081是映射到服务器上的端口,只在服务器本机有效。容器之间走内部网络,直接用Vaultwarden在容器里监听的80。
整条链路是这样的:
浏览器 → vault-demo.suversal.com → Cloudflare → Tunnel → cloudflared容器 → http://vaultwarden:80 → Vaultwarden容器
如果你的cloudflared不是用Docker跑的,而是直接装在服务器上,那就反过来,要填http://localhost:8081。
保存后Cloudflare会自动加一条DNS记录,指向这条隧道。如果这个子域名之前已经有记录,会提示冲突,先去DNS页面把旧记录删掉再保存:
浏览器打开你的域名https://vault-demo.suversal.com,能看到Vaultwarden的登录页就成功了:
三、注册主账号,然后马上关闭注册
点「Create account」注册你自己的账号:
主密码一定要记牢。
密码库是用主密码在本地加密的,服务器上只有密文。这是它安全的原因,也意味着主密码忘了,谁都救不回来,Vaultwarden没有重置功能。
注册完登录进去,就是熟悉的Bitwarden界面:
接着立刻关闭注册,不然知道你域名的人都能来注册:
cd ~/vaultwarden
sed -i 's/SIGNUPS_ALLOWED: "true"/SIGNUPS_ALLOWED: "false"/' docker-compose.yml
docker compose up -d
如果你是在Mac上操作,sed -i要改成sed -i '',或者直接用编辑器把true改成false。
改完环境变量要用docker compose up -d重建容器,只restart是不会生效的。
验证一下:
docker exec vaultwarden env | grep SIGNUPS
# SIGNUPS_ALLOWED=false
再用一个新邮箱去注册页试试,会直接提示「Registration not allowed」:
建议顺手在网页版的Settings→Security→Two-step login里给账号开两步验证,恢复代码单独存好。
到这里,服务端其实就已经搭好了:有自己的域名,有HTTPS,不开任何端口,也只有你一个人能登录。
下面讲怎么用起来:先把手机和浏览器连上,再把原来的密码导进来,最后说说怎么备份。
四、手机App和浏览器插件连接自建服务器
官方的Bitwarden App和浏览器插件都能直接用,区别只在登录前选一下服务器。
1. 下载官方客户端
装的都是Bitwarden官方的客户端,Vaultwarden本身没有App:
-
iPhone / iPad:App Store
-
安卓:Google Play
-
Mac:Mac App Store,装好后在Safari设置→扩展里勾上Bitwarden,就是Safari插件
-
Chrome:Chrome应用商店
Windows、Linux客户端和其他浏览器,在官方下载页都能找到。
2. 登录自建服务器
以浏览器插件为例,手机App的步骤也一样。安装后打开插件:
在登录页找到「登录到」→选自托管→服务器URL填你的域名,比如https://vault-demo.suversal.com→保存后用邮箱和主密码登录。注意只填域名,不要加:8081,也不要填IP。
登录后打开对应的网站,就会弹出自动填充提示。我这里已经导入了数据,导入方法见第五步:
3. 日常怎么用:手机和电脑
iPhone**:先把Bitwarden设成系统的自动填充,设置→通用→自动填充与密码,打开Bitwarden(iOS 17及以下在设置→密码→密码选项里)。
设好之后,在App或网页里点一下登录框,键盘上方就会出现Bitwarden里的账号,Face ID确认一下就填进去了。
如果系统自带的「密码」也开着,iOS会弹窗让你选用Bitwarden还是「密码」,保存新密码时也会问存到哪里。想省事的话,把「密码」关掉,只留Bitwarden。
顺手在App的设置→账户安全里打开「使用Face ID解锁」,不然每次都要输一遍主密码。
安卓:在系统设置里搜「自动填充服务」,选Bitwarden。各家手机的路径不太一样,直接搜最快。
电脑:主要靠浏览器插件。点一下登录框就会弹出匹配的账号,Chrome里也可以按Ctrl+Shift+L(Mac是Cmd+Shift+L)直接填进去。
注册新网站时,插件会提示要不要保存密码,点保存之后,手机上也会同步过来。插件里还能直接生成随机密码,长度、符号都能自己调。
4. 把URI匹配检测改成「主机」
Bitwarden默认的匹配方式是「基础域名」,也就是说wrap.suversal.com、nav.suversal.com、vault.suversal.com都会被当成同一个网站suversal.com。
这也是我用iCloud密码时最难受的一点:它只能这样匹配,没法改,也是我换到Vaultwarden的原因之一。
如果你和我一样,在同一个域名下挂了好几个自建服务,每次自动填充都会把所有子域名的账号全弹出来,很容易填错。
改法:设置→自动填充→默认URI匹配检测→主机。改完之后只有主机名完全一致才会匹配,互不干扰。
五、把已有密码数据导进来
用苹果全家桶的朋友,账号密码大概率都存在iCloud钥匙串里。不用一条条手动搬,Apple自带导出功能,导成CSV之后Vaultwarden可以直接导入。
1. 在Mac上导出
导出要在Mac上操作。iPhone上存的密码只要开了iCloud钥匙串同步,Mac上就都有,不用单独导。
macOS 15及以上:打开「密码」App→菜单栏文件→导出所有密码到文件…,用Touch ID或开机密码确认。
导出会得到一个Passwords.csv,里面所有密码都是明文,导完马上处理掉,后面会讲。
2. 导入到Vaultwarden
打开网页版,进入Tools→Import:
-
Vault:选My vault
-
File format:选Safari and macOS (csv)
-
Select the import file:选刚才导出的Passwords.csv
点Import就完成了。
能带过来的有:网站、用户名、密码、备注,还有验证码。
通行密钥(Passkey)带不过来,CSV里根本没有这一项,用到通行密钥的网站要在新密码库里重新添加一次。
3. 保障安全:导完立刻删掉CSV
这一步很关键也很容易忘:
-
把Passwords.csv删掉,废纸篓也要清空;
-
如果中途用隔空投送、微信或者网盘传过这个文件,那边也要删干净。
另外,导入只追加,不去重,同一个文件导两次就会有两份。导入前先确认库里是空的,或者只导一次。
从其他地方搬过来也一样
Chrome、Edge、1Password、官方Bitwarden都是同样的流程:先在原来的地方导出,再到Tools→Import里选对应的格式。
六、备份:常见的几种方案
密码库放在自己机器上,最怕硬盘损坏或者服务挂了,所以一定要有异地备份。
这一步每个人的环境差别很大,我说一下原则、我自己用的方案和其他常见做法,挑一个适合自己的就行。
先记住三个原则
-
备份整个vw-data目录,不只是db.sqlite3。附件、密钥这些文件都在里面。
-
数据库要先导出再复制。服务运行时直接复制sqlite文件,可能拷到写了一半的库。用sqlite3 db.sqlite3 ".backup 备份路径"导出一份一致的副本,或者先停容器再复制。
-
传出去之前先加密,解压密码另外存一份,不要只存在这个密码库里,不然服务器挂了,密码也跟着锁在里面。
我用的方案:每天自动加密推到GitHub
整体分两部分:先做一次性准备,之后每天凌晨自动跑。
大概步骤:
-
新建一个GitHub私有仓库,Visibility一定选Private,用来放备份。
-
给服务器生成一把Deploy Key,添加到这个仓库,勾上「Allow write access」。它只对这一个仓库有效,服务器万一被攻破,泄露的也只是这个仓库。
-
随机生成一个解压密码,存在服务器上给脚本用,同时自己另外存一份。
-
写一个备份脚本:sqlite3 .backup导出数据库→连同整个vw-data用7z AES-256加密→git push推到仓库,顺手删掉30天前的旧备份。
-
用cron设成每天凌晨3点自动执行,隔几天看一眼日志有没有报错。
跑起来之后,GitHub仓库里每天会多一个加密包:
用GitHub的好处是免费、自带历史版本,加密之后就算仓库泄露,没有解压密码也打不开。
装在Mac上的话
-
Time Machine:vw-data在用户目录下,Time Machine会自动备份进去。不过和前面原则里说的一样,直接拷运行中的数据库不一定完整,适合当第二份。
-
定时脚本放到iCloud云盘:每天用sqlite3导出、7z加密之后,丢进iCloud云盘的文件夹,自动同步上云。
-
定时脚本推到GitHub:和上面的方案一样,脚本在Mac上照样能用。
-
Mac上定时任务建议用launchd而不是cron:cron遇到电脑睡眠会直接跳过那一次,launchd会在唤醒后补跑。
最省事的兜底:手动导出
不管服务端怎么备份,都建议隔一段时间在网页版Tools→Export vault导出一份,格式选加密的JSON(密码保护),存到U盘或者另一个网盘里。
它不依赖服务器,哪天Vaultwarden彻底不用了,这份文件也能直接导入官方Bitwarden或者别的密码管理器。
备份完一定要恢复一次
没恢复过的备份不算备份。不管哪种方案,恢复的思路都一样:
停掉容器→解压备份→把原来的vw-data整个挪走→放入备份的vw-data→docker compose up -d。
注意是整个目录替换,不要直接覆盖进去。旧目录里有db.sqlite3-wal这类临时文件,留着的话会被套到恢复的库上,数据可能错乱。挪走的旧目录先别删,恢复出问题还能退回去。
我在演示机上模拟了一次硬盘损坏,把整个vw-data挪走,只靠GitHub上的备份恢复,用户和密码条目一条不少,直接用原来的主密码登录:
具体的备份脚本和恢复命令,有需要的朋友可以评论区留言,我再单独整理。
国内服务器的特别说明
这次演示用的是腾讯云上海的机器,比海外VPS多踩了几个坑,用国内机的朋友可以参考:
-
Docker Hub拉不动:要先配镜像加速。我这台机器已经配好了,拉Vaultwarden镜像只用了7秒。
-
GitHub能连但很慢:HTTPS经常超时,SSH没问题,备份要推到GitHub的话走SSH更稳。
-
为什么还是用Tunnel:国内服务器直接用80/443端口对外提供网站,域名需要备案。Tunnel不开入站端口,就没有这个问题,而且比直接暴露在公网上更安全。
-
代价是慢:流量要绕到Cloudflare的海外节点,我这台小水管第一次打开网页版要等半分钟左右。日常用App和插件影响不大,只是同步慢一点。
如果没有特殊需求,海外VPS或者家里的电脑会比国内云服务器省心很多。
最后检查一遍
搭完之后,对着这几条过一遍:
-
端口绑定的是127.0.0.1,公网直连端口打不开
-
SIGNUPS_ALLOWED已经改成false,并且用新邮箱试过注册不了
-
账号开了两步验证,恢复代码单独存好
-
导入用的CSV已经删掉,废纸篓也清空了
-
选好了一种备份方案,解压密码另外存了一份
-
真的从备份恢复过一次
另外两个常被问到的点:
-
怎么更新:隔一段时间在~/vaultwarden和~/cloudflared里各跑一次docker compose pull && docker compose up -d,新版客户端偶尔需要新版服务端。
-
管理后台/admin:不设置ADMIN_TOKEN时是关闭的,个人用不需要打开。
写在最后
到这里,一个完全由自己掌控的密码库就搭好了。总结一下整个过程:
| 步骤 | 做了什么 | 最容易踩的坑 |
|---|---|---|
| 部署Vaultwarden | 一个compose文件,Docker启动 | 端口要绑127.0.0.1,DOMAIN一定要填 |
| 发布到公网 | cloudflared单独部署,走Cloudflare Tunnel | Service URL填http://vaultwarden:80,不是localhost |
| 关闭注册 | SIGNUPS_ALLOWED改成false | 要用docker compose up -d重建,restart不生效 |
| 连接客户端 | 官方Bitwarden App和浏览器插件 | 登录前选自托管;URI匹配改成「主机」 |
| 导入密码 | Apple密码导出CSV,网页版导入 | 导完立刻删掉CSV |
| 备份 | 选一种方案,异地加密保存 | 解压密码另存一份,一定要恢复一次 |
整套搭下来,服务器不用开任何端口,也不用自己管证书,日常用起来和官方Bitwarden几乎没区别。
cloudflared单独部署之后,以后别的服务想发布出去,接上tunnel网络、再加一条路由就行。
感谢看到这里,如果这篇文章对你有帮助的话,欢迎关注、点赞、收藏。如果有其他想看的干货文章或者文章中存在错误的地方,也欢迎在评论区指出。我们下篇文章再见。

























