Vaultwarden VPS 自架密碼管理器設定與安全部署
使用 Docker 在 VPS 部署 Vaultwarden,先完成 HTTPS 才能連線,512 MB RAM 可運作;並設定 admin token、Fail2ban 與備份還原測試。
您正在建構的系統
一套完全由您掌控的密碼管理器:在反向代理後方執行一個輕量級容器的 Vaultwarden,由該代理處理 HTTPS 終止,並將手機、筆記型電腦與瀏覽器上的官方 Bitwarden 應用程式指向此服務。Vaultwarden 使用 Rust 重新實作了 Bitwarden 伺服器 API,並採用與 bitwarden.com 相同的協定,因此只要保持伺服器版本更新(新版客戶端有時需要對應的 Vaultwarden 版本:客戶端 2026.8.0 及更新版本需要 1.37.2),官方客戶端即可直接運作。相較於官方的多容器堆疊,它僅需約 100 MB 的記憶體。
安裝過程僅需十幾行的 Compose 設定。真正關鍵且容易出錯的環節有三點:在載入 Web vault 前必須先完成 TLS 設定;在建立個人帳號後應立即關閉公開註冊;最後,資料儲存卷必須進行備份並測試還原,因為該目錄存放了您所有的密碼。
先決條件與必須注意的實際問題
- 一台安裝 Docker Engine 與 Compose plugin 的 VPS。主機應是全新的 Ubuntu 24.04 KVM 主機,並具備 root 或 sudo 權限。512 MB RAM 確實足夠,1 GB 則較為寬裕。這是可執行的服務中最輕量的項目之一,位於值得自行代管的服務清單前段。不過,仍應依共用該主機的其他服務規劃規格:若在同一台 VPS 上部署PhotoPrism 或 Immich 等自行代管的相片庫,RAM 最低需求會提高到數 GB;Vaultwarden 幾乎不會增加負載。同樣的計算方式也適用於之後加上的媒體前端,因為將 Jellyfin 媒體庫整理成可瀏覽的 90 年代出租店,代表同一份資源預算還要負擔另一個持續運作的容器,以及轉碼所需的餘裕。
- 一個具有 A record 的網域;若使用 IPv6,也應設定 AAAA record,並將其指向
vault.example.comVPS。TLS 憑證會核發給這個確切的名稱,因此開始操作前,DNS 必須已能正確解析。 - 對網際網路開放 80 與 443 埠,並由反向代理終止連線,絕不能直接由 Vaultwarden 終止。80 埠僅用於 ACME 憑證驗證,以及將 HTTP 重新導向至 HTTPS。
- 首要且最容易踩到的問題是:Bitwarden 用戶端拒絕連線至未使用 HTTPS 的伺服器。不存在「先用 http 測試」這種做法,因為該流程無法運作;下一節會說明具體原因。
為何選擇 Vaultwarden 而非官方 Bitwarden stack
使用相同的客戶端,但資源佔用極低。官方標準的自架 Bitwarden 部署是一組容器(包含 MSSQL Express、Identity、Api、Admin 等),至少需要 2 GB RAM。Bitwarden 另提供 Bitwarden lite,這是一個單一 Docker 映像檔,至少需要 200 MB RAM,但其定位為個人使用與家庭實驗室,不適用於商業環境,且資料庫與備份需由使用者自行處理。Vaultwarden 是一個單一執行檔,預設將所有資料儲存在 SQLite 資料庫中,閒置時僅佔用數十 MB 記憶體。對於個人、家庭或小型團隊而言,這是顯而易見的選擇;由於它忠實實作了 Bitwarden API,您的資料可在 Vaultwarden 與 bitwarden.com 之間自由遷移。
您將失去的是大部分企業級功能:不支援 SCIM 配置(儘管 1.35.0 版本已加入 OpenID Connect SSO 支援),且身為營運者,修補程式更新、HTTPS 設定與備份皆需由您自行負責。本指南即涵蓋這三項工作。
為何 HTTPS 是必要條件
Bitwarden 網頁保險庫與瀏覽器擴充功能會在瀏覽器中使用 Web Crypto API (window.crypto.subtle) 來衍生您的加密金鑰。瀏覽器僅會在「安全環境」(即 HTTPS,或如 http://localhost 與 http://127.0.0.1 這類 loopback 位址的特殊情況)中開放 crypto.subtle。若透過明文 http://vault.example.com 傳輸,該 API 將會是 undefined,因此應用程式一旦嘗試衍生金鑰就會拋出錯誤,控制台會顯示:
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'importKey')頁面會卡住或顯示一般的加密錯誤,導致無法登入。桌面版、行動版與瀏覽器用戶端會針對自架 URL 執行檢查,若連線至 http(或無法連線的)端點,它們會拒絕連線並顯示:
This is not a recognized Bitwarden server. You may need to check with your provider or update your server.兩者原因相同:缺乏有效的 HTTPS。因此,我們必須優先架設 TLS,且絕不透過 http 開啟保險庫,即使只是為了快速查看也不行。
步驟 1:DNS 與反向代理(先處理 TLS)
將記錄指向您的 VPS,並確認解析到正確的位址:
dig +short vault.example.com輸出的那一行必須是您的 VPS IP。若為空白或位址錯誤,請修正 DNS 並等待 TTL 生效。名稱無法解析時,憑證簽發會失敗。
本指南使用 Traefik 作為 HTTPS 前端。Traefik 會自動簽發及續期 Let's Encrypt 憑證,並可直接整合 Compose。若您尚未執行 Traefik,請先依照 Traefik 反向代理與自動 TLS 設定 操作;該設定會建立外部 Docker network(下方的 proxy)與 ACME resolver(letsencrypt),供 Vaultwarden 服務連接。從 Vaultwarden 的角度來看,使用一般 nginx 搭配手動簽發的憑證,運作方式完全相同。
偏好使用 nginx 與 Certbot,而不是 Traefik? 將 Vaultwarden 放在 127.0.0.1:8080 上(在服務中加入 ports: ["127.0.0.1:8080:80"],並移除 Traefik labels),接著簽發憑證並將代理流量轉送至該服務。憑證部分請參閱 使用 Certbot 與 nginx 簽發 Let's Encrypt 憑證。需要額外處理的重點,是 notifications 路徑上的 WebSocket upgrade:
server {
listen 443 ssl;
server_name vault.example.com;
client_max_body_size 525M;
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_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}請注意 X-Real-IP 這一行。它讓 Fail2ban 之後能看到實際的攻擊者,而不是 127.0.0.1。無論前端使用 Traefik 或 nginx,本指南的其他部分都相同。
第 2 步,Compose 檔案
先建立專案目錄。本指南使用 /opt/vaultwarden,讓 Compose 專案名稱以及資料磁碟區名稱 vaultwarden_vw-data 保持可預測;下方的 Fail2ban 與備份步驟都依賴這個確切名稱。
sudo mkdir -p /opt/vaultwarden
cd /opt/vaultwarden在該目錄中建立一個 .env,以及 Compose 檔案。
# .env
ADMIN_TOKEN=paste-a-strong-token-here使用 openssl rand -base64 48 產生該權杖,然後貼上。下一節會說明更安全的雜湊形式;一開始使用長的隨機字串即可。
# docker-compose.yml
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
environment:
DOMAIN: "https://vault.example.com"
SIGNUPS_ALLOWED: "true" # closed in Step 4, keep true just to register
ADMIN_TOKEN: "${ADMIN_TOKEN}"
IP_HEADER: "X-Forwarded-For" # X-Real-IP if your proxy sends that instead
LOG_FILE: "/data/vaultwarden.log"
LOG_LEVEL: "warn"
volumes:
- vw-data:/data
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.vw.rule=Host(`vault.example.com`)"
- "traefik.http.routers.vw.entrypoints=websecure"
- "traefik.http.routers.vw.tls.certresolver=letsencrypt"
- "traefik.http.services.vw.loadbalancer.server.port=80"
volumes:
vw-data:
networks:
proxy:
external: true這個檔案中有兩項設定支撐整體設計。檔案中沒有 ports: 對映,因此 Vaultwarden 只能透過 Traefik 及其 TLS 連線;在主機上公開其連接埠,會導致使用者意外透過 http 提供 vault。DOMAIN 必須是完整的公開 HTTPS URL:系統會將它寫入附件連結、WebAuthn 2FA 與 notifications endpoint,因此即使網站可以載入,URL 錯誤或使用 http 仍會使這些功能失效。latest 標籤是對一般不得使用 latest 規則的刻意例外。Vaultwarden 將穩定版本以單一持續更新映像檔提供,並以 :testing 作為獨立的預發布通道,因此請依計畫更新,並在 pull 前快速查看版本資訊。這項例外的適用範圍很窄;大多數長期執行的容器最好固定使用確切標籤,這能讓 在同一 VPS 上持續運作的自架代理程式 在重新開機與 pull 後維持可預測性。
啟動服務並監控日誌:
docker compose up -d
docker compose logs -f vaultwarden正確啟動後,結尾會出現類似 Rocket has launched from http://0.0.0.0:80 的行。等待 Traefik 幾秒以取得憑證,然後載入 https://vault.example.com;此時應會看到 Bitwarden web vault,瀏覽器顯示有效的鎖頭,且沒有憑證警告。
步驟 3:強健的 ADMIN_TOKEN 與 $$ 陷阱
ADMIN_TOKEN 用於保護 /admin,此面板可讀取執行個體上的所有使用者與設定,請將其視為 root 密碼處理。有兩種設定方式。
簡單的方式是使用您先前透過 openssl rand -base64 48 產生的隨機字串。由於 base64 永遠不會包含 $,因此可直接放入 .env 而無需跳脫。
進階的強化方式是使用 Argon2 PHC 雜湊值,這樣明文權杖就不會儲存在磁碟上。請使用相同的映像檔產生雜湊:
docker run --rm -it vaultwarden/server /vaultwarden hash --preset owasp系統會提示您輸入兩次,並輸出以 $argon2id$v=19$... 開頭的字串。這是導致許多人浪費一小時的陷阱:Docker Compose 會將 $ 視為變數插值,因此若您將雜湊值直接貼到 Compose 檔案的 environment: 下方,必須將所有的 $ 加倍為 $$。在該處加上引號是可以的;關鍵在於跳脫處理:
environment:
ADMIN_TOKEN: $$argon2id$$v=19$$m=19456,t=2,p=1$$c29tZXNhbHQ$$RdescudvJCsgt3ub+b+dWRWJTmaaJObG若您偏好將雜湊值保留在 .env 中,規則則相反:保留單個 $ 符號,將整個值以單引號 (ADMIN_TOKEN='$argon2id$v=19$...') 包覆,Compose 檔案中現有的 ${ADMIN_TOKEN} 行即可讀取。切勿在 .env 中將 $ 加倍。若您在 Compose 檔案中保留單個 $ 符號,Compose 會發出 The "argon2id" variable is not set 警告並將權杖清空,隨後 /admin 會拒絕您正確的密碼。請執行 docker compose up -d,並將您在提示字元輸入的明文密碼儲存在您自己的密碼管理員中。
步驟 4:註冊帳戶,然後關閉註冊
使用 SIGNUPS_ALLOWED: "true" 開啟 https://vault.example.com,按一下 Create account,並使用電子郵件地址和高強度主密碼註冊。主密碼無法復原,也沒有重設功能,因此請先將它儲存在可靠的位置。
現在關閉註冊功能。編輯 Compose 檔案,停用註冊:
SIGNUPS_ALLOWED: "false"使用 docker compose up -d 重新套用設定。這項強化措施不能延後。註冊功能開啟時,任何找到 URL 的人都能在您的伺服器上建立帳戶,搜尋引擎爬蟲也可能找到該 URL。他們無法讀取您的保存庫,但會消耗資源,並將您的私有執行個體變成公開服務。若 /admin 列出您從未建立的帳戶,就表示您仍未關閉註冊功能。
日後若要新增家人或團隊成員,請使用 /admin 中的 Invite User 按鈕,不必重新開放公開註冊。此流程需要先設定 SMTP,受邀者才能收到邀請連結。
步驟 5:存取 /admin
瀏覽至 https://vault.example.com/admin 並輸入明文管理員權杖(即隨機字串或您雜湊前的密碼,而非雜湊值本身)。進入後,您可以列出使用者、調整設定、發送測試郵件以及建立資料庫快照。
若頁面顯示 The admin panel is disabled, please configure the 'ADMIN_TOKEN' variable to enable it,表示 ADMIN_TOKEN 為空或未設定,這會完全停用管理面板;若您不需要此功能,這也是一種合理的選擇。若頁面載入但拒絕您的權杖,請參閱下方故障清單中的 $$ 跳脫字元陷阱。若忘記權杖,系統並無復原提示;請編輯 .env 或 Compose 檔案,設定一組新的權杖,並執行 docker compose up -d。
步驟 6,連線 Bitwarden 用戶端
每個官方用戶端都能連線至自架伺服器。因此,請從一般應用程式商店安裝 Bitwarden 桌面、行動或瀏覽器用戶端;不需要特殊的 Vaultwarden 組建。
登入前,請在登入畫面開啟設定齒輪(標示為 Self-hosted 或 Region → Self-hosted),將 Server URL 設為 https://vault.example.com,然後儲存。接著使用註冊時提供的電子郵件地址與主密碼登入。用戶端應會立即連線,並提供填入及儲存認證資料的功能。
如果用戶端顯示 This is not a recognized Bitwarden server. You may need to check with your provider or update your server.,表示 URL 錯誤、使用了 http,或憑證不受信任。請先確認在瀏覽器中開啟 https://vault.example.com 時沒有錯誤。其他裝置上的更新速度較慢,原因是 WebSocket 推送;下文會說明相關設定。
步驟 7:為登入端點設定 Fail2ban jail
Vaultwarden 會將每次登入失敗記錄至 LOG_FILE 指定的檔案,這正是暴力破解防護機制所需的資訊。若您尚未安裝 Fail2ban,請參閱 Fail2ban SSH 強固指南 了解安裝與基礎設定;在此我們將為 Vaultwarden 新增一個 jail。
首先找出 Docker 命名卷(named volume)在主機上的實際路徑,以便 Fail2ban 讀取日誌:
docker volume inspect vaultwarden_vw-data --format '{{ .Mountpoint }}'該指令會輸出類似 /var/lib/docker/volumes/vaultwarden_vw-data/_data 的路徑;日誌檔案位於其中的 vaultwarden.log。接著建立過濾器:
# /etc/fail2ban/filter.d/vaultwarden.conf
[Definition]
failregex = ^.*Username or password is incorrect\. Try again\. IP: <ADDR>\. Username:.*$
ignoreregex =並建立 jail:
# /etc/fail2ban/jail.d/vaultwarden.local
[vaultwarden]
enabled = true
filter = vaultwarden
logpath = /var/lib/docker/volumes/vaultwarden_vw-data/_data/vaultwarden.log
banaction = iptables-allports
chain = DOCKER-USER
maxretry = 5
findtime = 600
bantime = 3600執行 sudo systemctl restart fail2ban 重新載入設定,並以 sudo fail2ban-client status vaultwarden 確認狀態。
有三個 Docker 細節決定了此防護是否有效。首先,若日誌顯示每次失敗嘗試的來源皆為 IP: 127.0.0.1 或您代理伺服器的位址,代表 Vaultwarden 封鎖的是代理伺服器本身。請將 IP_HEADER 設定為代理伺服器實際傳送的標頭(Traefik 為 X-Forwarded-For,上述 nginx 區塊為 X-Real-IP,若位於 Cloudflare 後方則為 CF-Connecting-IP)。自 1.37.4 版本起,透過 X-Forwarded-For,Vaultwarden 會選取最右側且不在 IP_HEADER_TRUSTED_PROXIES 清單中的位址(預設為 local,涵蓋同一主機或 Docker 網路上的代理伺服器);若您的代理伺服器前方還有 CDN,請將其位址一併列入。其次,正確的 iptables chain 取決於您的代理伺服器:若 Traefik 以容器形式執行並發布埠號,流量會經過 Docker 的 FORWARD 路徑,因此封鎖規則必須如上設定於 DOCKER-USER;但若您在步驟 1 選擇了 host-nginx 選項,連線會在主機的 INPUT chain 終止,此時 DOCKER-USER 封鎖規則將無法偵測到流量,這種情況下請刪除 chain = DOCKER-USER 行,讓 Fail2ban 使用預設的 INPUT chain。第三,請使用 banaction = iptables-allports 而非預設的埠號模式;此 jail 未定義任何埠號,而 DOCKER-USER 中的全埠封鎖能徹底阻擋攻擊者存取主機上所有已發布的服務。
步驟 8,備份保險庫並執行還原測試
vw-data 儲存卷即為您的密碼管理器。它包含 db.sqlite3(所有項目)、attachments/ 與 sends/ 目錄、用於簽署登入工作階段的 rsa_key.* 檔案,以及來自管理面板的 config.json。若備份遺漏其中任何項目,還原時將會失敗。
在 Vaultwarden 寫入資料時複製 db.sqlite3 可能會導致檔案損毀或寫入不完整,因此請執行冷備份(cold snapshot),停機時間僅需數秒:
#!/usr/bin/env bash
set -euo pipefail
STAMP=$(date +%F)
DEST=/root/vw-backups
VOL=$(docker volume inspect vaultwarden_vw-data --format '{{ .Mountpoint }}')
mkdir -p "$DEST"
docker compose -f /opt/vaultwarden/docker-compose.yml stop vaultwarden
tar czf "$DEST/vw-$STAMP.tgz" -C "$VOL" .
docker compose -f /opt/vaultwarden/docker-compose.yml start vaultwarden請將此指令加入 cron 每日執行,並將 .tgz 複製到伺服器外部;若備份僅存於受保護的伺服器上,則不具備備份意義。建議採用 每日透過 restic 備份至其他伺服器或物件儲存 的方式,此方法可自動加密封存檔並對重複的快照進行去重。管理面板中的 Backup Database 按鈕僅能針對 SQLite 檔案進行熱備份,但會遺漏附件與金鑰。
現在執行區分「有效備份」與「僅存希望」的關鍵步驟:執行一次還原並驗證其有效性:
mkdir -p /tmp/vw-restore
tar xzf /root/vw-backups/vw-2026-07-15.tgz -C /tmp/vw-restore
docker run --rm -p 127.0.0.1:8888:80 -v /tmp/vw-restore:/data vaultwarden/server從您的筆記型電腦透過 ssh -L 8888:127.0.0.1:8888 you@your-vps 建立通道,並開啟 http://localhost:8888(或 http://127.0.0.1:8888)。由於 loopback 位址被視為安全上下文,因此 crypto.subtle 可正常運作,保險庫可透過純 http 進行解密,這是唯一允許此類連線的情況。使用您的主密碼登入並確認項目皆已存在:若確認無誤,代表您的資料庫、RSA 金鑰與主密碼皆已完整備份,未來若需在新的 VPS 上重建,僅需幾分鐘即可完成。最後使用 Ctrl-C 停止容器並刪除 /tmp/vw-restore。請養成使用此通道存取伺服器上不應暴露於網際網路的管理介面之習慣,這同樣適用於存取連接埠 5173 上的 自架 open-kritt 安全掃描器。
故障模式與對應訊息
瀏覽器主控台顯示 Cannot read properties of undefined (reading 'importKey')。 Vault 透過 http 載入,導致 crypto.subtle 未定義;請僅透過 https:// 存取,並在代理伺服器設定 HTTP 轉 HTTPS 的重新導向。
用戶端顯示 This is not a recognized Bitwarden server...。 Server URL 設定為 http、拼字錯誤或憑證不受信任;請確認 https://vault.example.com 顯示有效的鎖頭圖示,並在用戶端的自架設定中重新輸入。
/admin 拒絕正確的密碼。 Argon2 雜湊值遺失了跳脫字元,Compose 檔案中的每個 $ 都必須寫成 $$(但在 .env 的單引號內只需單個 $),或者您輸入的是雜湊值本身而非其代表的明文。
跨裝置同步緩慢;主控台顯示 WebSocket connection to 'wss://vault.example.com/notifications/hub' failed。 代理伺服器未轉發 Upgrade/Connection 標頭;Traefik 會自動處理此項,但 nginx 需要步驟 1 中的兩行 upgrade 設定。Vault 仍可運作,僅會在開啟時進行同步。舊有的專用連接埠 3012 已於 v1.31.0 移除,因此無需額外的 WebSocket 路由。
Fail2ban 回報封鎖但攻擊者持續連線。 這是因為 IP_HEADER 設定錯誤導致封鎖了 127.0.0.1,或是封鎖規則位於錯誤的 iptables 鏈中,請設定 chain = DOCKER-USER 與 banaction = iptables-allports。
升級
拉取新映像並重新建立容器;具名磁碟區與所有資料都會保留:
docker compose pull
docker compose up -dVaultwarden 經常發布新版本。請監看專案的版本資訊,不要固定 patch 版本,因為部分版本包含移轉注意事項。在進行任何重大升級前,先建立最新備份;若需要回復,可以將 tarball 還原至新的磁碟區。
FAQ
Vaultwarden 與 Bitwarden 是一樣的嗎?
這是一個相容的獨立伺服器,並非官方版本。Vaultwarden 使用 Rust 語言重新實作了 Bitwarden 伺服器 API,因此官方的桌面版、行動版、瀏覽器擴充功能與 CLI 客戶端皆可與其搭配使用,且資源消耗遠低於官方架構。由於保險庫格式相同,您可以透過匯出與匯入資料在兩者間進行遷移。
我真的需要 HTTPS 嗎?還是可以在區域網路使用 http?
除了本機迴路測試外,您必須使用 HTTPS。Bitwarden 網頁版保險庫與瀏覽器擴充功能皆使用瀏覽器的 Web Crypto API,該 API 僅在安全環境下運作;若使用明文 http,客戶端會拋出 Cannot read properties of undefined 錯誤並導致無法登入。唯一可運作的 http 位址僅限於如 http://localhost 或 http://127.0.0.1 等本機迴路位址,這也是為何步驟 8 的還原測試必須使用 SSH 通道的原因。
如何防止陌生人在我的伺服器註冊?
在建立好您的帳號後,請立即在 Compose 檔案中設定 SIGNUPS_ALLOWED: "false" 並執行 docker compose up -d。此後,請透過 /admin 中的「邀請使用者」(Invite User)按鈕新增人員,此功能需要設定 SMTP 才能讓對方收到邀請連結。請定期檢查管理員使用者列表,確認是否有未預期的帳號出現。
如何備份我的 Vaultwarden 保險庫?
請暫時停止容器,並將整個 vw-data 儲存卷、db.sqlite3、attachments/、sends/、config.json 以及 rsa_key.* 檔案進行封存,接著將封存檔複製到伺服器外部,建議設定為每日排程執行。在伺服器執行期間直接複製 SQLite 檔案可能會導致快照損毀,因此請務必在離線狀態下進行。最重要的是,請務必將備份還原至測試用容器並登入一次,以確保在依賴該備份前,它是真實且可用的。
自架密碼庫真的安全嗎?
是的,只要您執行本指南涵蓋的三項要點:真實的 HTTPS、關閉註冊並設定強效管理員權杖(admin token),以及經過驗證的備份。您的保險庫在客戶端即已透過主密碼加密,因此即使是伺服器也無法以明文形式讀取您的密碼;即便 db.sqlite3 被竊取,若無主密碼也無法解開內容。不過,伺服器仍會儲存帳號電子郵件以及由主密碼產生的加鹽雜湊值,攻擊者可能會嘗試離線破解,因此請務必保護好資料庫檔案及其所有備份。自架的代價在於修補與備份皆由您自行負責,這也是為何 Fail2ban 與還原演練在此處並非選用項目。一旦完成上述設定,深入了解自架保險庫的潛在攻擊面將是實用的下一步,因為當資料項目本身已在客戶端加密後,您需要防禦的重點便轉移至管理員權杖與備份封存檔。