Cài Vaultwarden trên VPS bằng Docker, bật HTTPS
Cài Vaultwarden tương thích Bitwarden trên VPS với Docker, HTTPS trước lần mở đầu, admin token, Fail2ban và backup đã test restore cho data volume.
Bạn sẽ xây dựng gì
Một password manager do bạn toàn quyền quản lý: Vaultwarden chạy trong một container nhỏ phía sau reverse proxy xử lý TLS, cùng các ứng dụng Bitwarden chính thức trên điện thoại, laptop và trình duyệt kết nối đến đó. Vaultwarden triển khai lại Bitwarden server API bằng Rust và sử dụng cùng protocol với bitwarden.com, nên các client chính thức hoạt động mà không cần thay đổi, miễn là bạn luôn cập nhật server. Các bản phát hành client mới đôi khi yêu cầu bản Vaultwarden tương ứng: cần Vaultwarden 1.37.2 cho client 2026.8.0 trở lên. Tuy nhiên, Vaultwarden chỉ dùng khoảng 100 MB RAM thay vì stack chính thức gồm nhiều container.
Phần cài đặt chỉ gồm khoảng 12 dòng Compose. Có 3 việc thực sự quan trọng và dễ gây lỗi: TLS phải hoạt động trước khi bạn mở web vault lần đầu, phải đóng đăng ký public ngay khi tài khoản của bạn được tạo, và phải backup rồi test restore data volume. Thư mục đó chứa toàn bộ password của bạn.
Điều kiện cần và những điểm dễ vướng cần biết
- Một VPS đã cài Docker Engine và Compose plugin, chạy trên Ubuntu 24.04 mới cài trên máy KVM, có tài khoản root hoặc sudo. 512 MB RAM thực sự là đủ; 1 GB sẽ thoải mái hơn. Đây là một trong những dịch vụ nhẹ nhất bạn có thể chạy và nằm gần đầu danh sách ngắn các dịch vụ đáng tự host. Tuy nhiên, hãy chọn cấu hình VPS dựa trên mọi dịch vụ khác chạy chung: đưa thư viện ảnh tự host như PhotoPrism hoặc Immich lên cùng VPS sẽ nâng mức RAM tối thiểu lên vài GB, trong khi Vaultwarden hầu như không làm tăng mức này. Cách tính tương tự áp dụng cho các media front end bạn gắn thêm sau này, vì biến thư viện Jellyfin thành một cửa hàng cho thuê băng đĩa thập niên 90 có thể đi dạo qua đồng nghĩa với việc chạy thêm một container luôn bật và cần thêm headroom cho transcoding trong cùng ngân sách.
- Một domain có bản ghi A (và AAAA nếu bạn dùng IPv6) trỏ
vault.example.comđến VPS. TLS certificate được cấp cho chính xác tên này, nên DNS phải resolve được trước khi bắt đầu. - Mở port 80 và 443 ra Internet, do reverse proxy xử lý, không bao giờ để Vaultwarden trực tiếp xử lý. Port 80 chỉ dùng cho ACME certificate challenge và redirect từ HTTP sang HTTPS.
- Điểm dễ vướng lớn nhất cần biết ngay từ đầu: các Bitwarden client sẽ từ chối kết nối đến server không dùng HTTPS. Không có cách “thử bằng http trước”; cách đó không hoạt động vì một lý do cụ thể sẽ được giải thích ở phần tiếp theo.
Vì sao chọn Vaultwarden thay vì stack Bitwarden chính thức
Cùng dùng các client nhưng nhẹ hơn rất nhiều. Cách triển khai Bitwarden self-hosted chính thức tiêu chuẩn chạy một bundle gồm nhiều container như MSSQL Express, Identity, Api, Admin và các thành phần khác, đồng thời cần ít nhất 2 GB RAM. Bitwarden cũng cung cấp Bitwarden lite, một Docker image duy nhất cần ít nhất 200 MB RAM, nhưng sản phẩm này dành cho mục đích cá nhân và home lab, không dành cho doanh nghiệp; bạn cũng phải tự quản lý hoàn toàn database và các bản backup. Vaultwarden là một binary duy nhất, mặc định lưu mọi dữ liệu trong database SQLite và khi idle chỉ dùng vài chục megabyte. Với một người, một gia đình hoặc một team nhỏ, đây là lựa chọn rõ ràng. Vì triển khai đúng Bitwarden API, dữ liệu của bạn vẫn có thể chuyển đổi giữa Vaultwarden và bitwarden.com.
Đổi lại, bạn không có phần lớn các tính năng enterprise: không có SCIM provisioning (dù hỗ trợ OpenID Connect SSO đã có từ 1.35.0). Bạn là operator, nên việc patch, cấu hình HTTPS và backup là trách nhiệm của bạn. Guide này tập trung vào ba công việc đó.
Vì sao HTTPS là bắt buộc
Bitwarden web vault và các browser extension tạo encryption key trong browser bằng Web Crypto API (window.crypto.subtle). Browser chỉ cung cấp crypto.subtle trong secure context, qua HTTPS hoặc trong trường hợp đặc biệt là các loopback address như http://localhost và http://127.0.0.1. Qua http://vault.example.com thuần, tính năng này undefined, nên ngay khi app tạo key, nó sẽ báo lỗi và console hiển thị:
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'importKey')Trang bị treo hoặc hiển thị lỗi crypto chung chung, và không ai đăng nhập được. Các client desktop, mobile và browser tự kiểm tra self-hosted URL. Nếu endpoint dùng http hoặc không thể truy cập, chúng sẽ từ chối với lỗi:
This is not a recognized Bitwarden server. You may need to check with your provider or update your server.Cả hai trường hợp đều có cùng nguyên nhân: không có HTTPS hợp lệ. Vì vậy, trước tiên chúng ta thiết lập TLS và không bao giờ mở vault qua http, kể cả chỉ mở một lần để kiểm tra nhanh.
Bước 1, DNS và reverse proxy (ưu tiên TLS)
Trỏ record đến VPS của bạn và xác nhận record phân giải đến đúng địa chỉ:
dig +short vault.example.comDòng lệnh in ra phải là IP của VPS. Nếu dòng này trống hoặc sai, hãy sửa DNS và chờ hết TTL. Việc cấp certificate sẽ thất bại nếu hostname không phân giải được.
Guide này dùng Traefik cho frontend HTTPS. Traefik tự động cấp và gia hạn certificate Let's Encrypt, đồng thời tích hợp trực tiếp với Compose. Nếu bạn chưa chạy Traefik, hãy làm theo hướng dẫn thiết lập reverse proxy Traefik và TLS tự động trước. Thiết lập này tạo một Docker network bên ngoài (proxy bên dưới) và một ACME resolver (letsencrypt) để service Vaultwarden kết nối vào. nginx thông thường với certificate cấp thủ công cũng hoạt động tương tự ở phía Vaultwarden.
Bạn muốn dùng nginx và Certbot thay vì Traefik? Đặt Vaultwarden vào 127.0.0.1:8080 (thêm ports: ["127.0.0.1:8080:80"] vào service và xóa các label của Traefik), sau đó cấp certificate và proxy đến service này. Phần certificate được trình bày trong hướng dẫn cấp certificate Let's Encrypt bằng Certbot và nginx. Phần bổ sung quan trọng là cấu hình WebSocket upgrade trên path notifications:
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";
}
}Chú ý dòng X-Real-IP. Dòng này cho phép Fail2ban sau đó nhìn thấy IP thật của attacker thay vì 127.0.0.1. Mọi phần khác trong guide đều giống nhau, dù phía trước dùng Traefik hay nginx.
Bước 2, file Compose
Trước tiên, tạo thư mục project. Guide này dùng /opt/vaultwarden để tên project Compose và do đó cả data volume là vaultwarden_vw-data, có thể dự đoán được; các bước cấu hình Fail2ban và backup bên dưới phụ thuộc vào đúng tên này.
sudo mkdir -p /opt/vaultwarden
cd /opt/vaultwardenTrong thư mục đó, tạo một .env cho admin secret và file Compose.
# .env
ADMIN_TOKEN=paste-a-strong-token-hereTạo token bằng openssl rand -base64 48 rồi dán token vào file. (Phần tiếp theo sẽ hướng dẫn dạng hash mạnh hơn; để bắt đầu, một chuỗi ngẫu nhiên dài là đủ.)
# 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: trueCó 2 điểm trong file này quyết định toàn bộ thiết kế. Không có mapping ports:, nên chỉ có thể truy cập Vaultwarden thông qua Traefik và TLS của Traefik; publish port của Vaultwarden trên host sẽ khiến người dùng vô tình phục vụ vault qua http. Ngoài ra, DOMAIN phải là URL HTTPS public đầy đủ: giá trị này được tích hợp vào link attachment, WebAuthn 2FA và endpoint notifications, nên giá trị sai hoặc dùng http sẽ làm các chức năng đó hỏng dù website vẫn tải được. Tag latest là ngoại lệ có chủ ý đối với quy tắc thông thường là không bao giờ dùng latest. Vaultwarden phát hành các bản stable dưới dạng một image rolling duy nhất, còn :testing là channel pre-release riêng. Vì vậy, hãy chủ động update và đọc lướt release notes trước khi pull. Tuy nhiên, ngoại lệ này chỉ áp dụng trong phạm vi hẹp: phần lớn container chạy lâu dài nên được pin vào một tag chính xác. Đây là cách giữ cho một agent luôn chạy được self-host trên cùng VPS ổn định qua các lần reboot và pull.
Khởi động stack và theo dõi log:
docker compose up -d
docker compose logs -f vaultwardenNếu khởi động đúng, log sẽ kết thúc bằng một dòng như Rocket has launched from http://0.0.0.0:80. Chờ Traefik vài giây để lấy certificate, sau đó mở https://vault.example.com. Bạn sẽ thấy Bitwarden web vault với biểu tượng ổ khóa hợp lệ và không có cảnh báo certificate.
Bước 3, ADMIN_TOKEN mạnh và bẫy $$
ADMIN_TOKEN bảo vệ /admin, panel có thể đọc mọi user và setting trên instance của bạn, vì vậy hãy coi nó như root password. Có 2 dạng dùng được.
Dạng đơn giản là chuỗi ngẫu nhiên bạn đã tạo bằng openssl rand -base64 48. Vì base64 không bao giờ chứa $, bạn có thể đưa chuỗi này thẳng vào .env mà không cần escape.
Dạng an toàn hơn là Argon2 PHC hash, nên plaintext token không bao giờ được lưu trên disk. Tạo hash bằng cùng image:
docker run --rm -it vaultwarden/server /vaultwarden hash --preset owaspLệnh này yêu cầu nhập 2 lần rồi in ra một chuỗi bắt đầu bằng $argon2id$v=19$.... Đây là bẫy khiến nhiều người mất cả giờ: Docker Compose coi $ là phép nội suy biến, nên nếu dán hash trực tiếp dưới environment: trong Compose file, bạn phải nhân đôi mọi $ thành $$. Đặt value trong dấu quote ở đó cũng được; điều quan trọng là phải escape:
environment:
ADMIN_TOKEN: $$argon2id$$v=19$$m=19456,t=2,p=1$$c29tZXNhbHQ$$RdescudvJCsgt3ub+b+dWRWJTmaaJObGNếu muốn giữ hash trong .env, quy tắc sẽ ngược lại: giữ nguyên các dấu $ đơn, đặt toàn bộ value trong dấu nháy đơn (ADMIN_TOKEN='$argon2id$v=19$...'), rồi dòng ${ADMIN_TOKEN} có sẵn trong Compose file sẽ đọc value đó. Không bao giờ nhân đôi $ trong .env. Nếu để các dấu $ đơn trong chính Compose file, Compose sẽ cảnh báo The "argon2id" variable is not set và xóa token, sau đó /admin sẽ từ chối password đúng của bạn. Chạy docker compose up -d, rồi lưu plaintext bạn đã nhập tại prompt trong password store riêng của bạn.
Bước 4, đăng ký tài khoản rồi khóa đăng ký
Dùng SIGNUPS_ALLOWED: "true" để mở https://vault.example.com, nhấp Create account, rồi đăng ký bằng email và một master password mạnh. Không thể khôi phục master password này và cũng không có chức năng reset, vì vậy hãy lưu nó ở nơi bền vững trước.
Bây giờ hãy đóng đăng ký. Chỉnh sửa file Compose để tắt đăng ký:
SIGNUPS_ALLOWED: "false"Áp dụng lại bằng docker compose up -d. Đây không phải việc hardening có thể trì hoãn. Nếu để mở, bất kỳ ai tìm thấy URL, kể cả crawler, đều có thể tạo tài khoản trên server của bạn. Họ không thể đọc vault của bạn, nhưng vẫn tiêu tốn tài nguyên và biến instance riêng của bạn thành một service mở. Dấu hiệu cho thấy bạn vẫn để mở: /admin liệt kê các tài khoản mà bạn chưa từng tạo.
Để thêm thành viên gia đình hoặc đồng đội sau này mà không mở lại đăng ký công khai, hãy dùng nút Invite User trong /admin; cách này yêu cầu đã cấu hình SMTP để người được mời nhận được link của họ.
Bước 5, truy cập /admin
Mở https://vault.example.com/admin và nhập admin token dạng plaintext (chuỗi ngẫu nhiên hoặc password bạn đã hash, không phải hash). Trong đó, bạn có thể liệt kê user, điều chỉnh setting, gửi email kiểm tra và tạo snapshot cho database.
Nếu trang hiển thị The admin panel is disabled, please configure the 'ADMIN_TOKEN' variable to enable it, ADMIN_TOKEN đang rỗng hoặc chưa được set. Khi đó panel bị tắt hoàn toàn. Đây cũng là lựa chọn hợp lệ nếu bạn không bao giờ cần dùng panel này. Nếu panel tải được nhưng từ chối token, xem lỗi escaping của $$ trong danh sách lỗi bên dưới. Quên token? Không có prompt khôi phục. Hãy sửa .env hoặc file Compose, đặt token mới rồi docker compose up -d.
Bước 6, kết nối các client Bitwarden
Mọi client chính thức đều có thể kết nối đến server self-hosted. Vì vậy, hãy cài client Bitwarden cho desktop, mobile hoặc browser từ các store thông thường. Bạn không cần bản build Vaultwarden riêng.
Trước khi đăng nhập, mở biểu tượng bánh răng cài đặt trên màn hình đăng nhập (có nhãn Self-hosted hoặc Region → Self-hosted), đặt Server URL thành https://vault.example.com rồi lưu. Sau đó đăng nhập bằng email và master password đã đăng ký. Client sẽ kết nối ngay và đề nghị tự động điền, lưu credential.
Nếu client hiển thị This is not a recognized Bitwarden server. You may need to check with your provider or update your server., URL có thể sai, đang dùng http hoặc certificate không được tin cậy. Hãy kiểm tra lại để đảm bảo https://vault.example.com tải bình thường trong browser trước. Việc cập nhật chậm trên các thiết bị khác là do WebSocket push; phần dưới sẽ giải thích vấn đề này.
Bước 7, tạo một jail Fail2ban cho endpoint đăng nhập
Vaultwarden ghi lại mọi lần đăng nhập thất bại vào file được đặt bởi LOG_FILE. Đây chính xác là dữ liệu cần cho cơ chế chống brute-force. Nếu bạn chưa chạy Fail2ban, phần cài đặt và cấu hình cơ bản có trong hướng dẫn hardening SSH bằng Fail2ban; ở đây chúng ta chỉ thêm một jail cho vault.
Trước tiên, tìm vị trí của named volume trên host để Fail2ban có thể đọc log:
docker volume inspect vaultwarden_vw-data --format '{{ .Mountpoint }}'Lệnh này sẽ in ra nội dung tương tự /var/lib/docker/volumes/vaultwarden_vw-data/_data; file log nằm tại vaultwarden.log bên trong đó. Tạo filter:
# /etc/fail2ban/filter.d/vaultwarden.conf
[Definition]
failregex = ^.*Username or password is incorrect\. Try again\. IP: <ADDR>\. Username:.*$
ignoreregex =Sau đó tạo 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 = 3600Reload bằng sudo systemctl restart fail2ban và xác nhận bằng sudo fail2ban-client status vaultwarden.
Có 3 chi tiết về Docker quyết định cấu hình này có bảo vệ được hệ thống hay không. Thứ nhất, nếu log hiển thị IP: 127.0.0.1 hoặc địa chỉ của proxy trong mọi lần đăng nhập thất bại, Vaultwarden đang ban proxy. Hãy đặt IP_HEADER thành header mà proxy thực sự gửi, dùng X-Forwarded-For cho Traefik, X-Real-IP cho block nginx ở trên và CF-Connecting-IP khi chạy phía sau Cloudflare. Từ 1.37.4, khi dùng X-Forwarded-For, Vaultwarden sẽ lấy địa chỉ ngoài cùng bên phải không nằm trong IP_HEADER_TRUSTED_PROXIES (mặc định là local, bao phủ proxy chạy trên cùng host hoặc cùng Docker network). Vì vậy, nếu có CDN đứng trước proxy, hãy thêm các địa chỉ của CDN vào đó.
Thứ hai, chain iptables phù hợp phụ thuộc vào proxy bạn dùng. Khi Traefik chạy dưới dạng container với các port được publish, traffic đi qua path FORWARD của Docker. Vì vậy, rule ban phải nằm trong DOCKER-USER như ở trên. Nhưng nếu bạn chọn tùy chọn host-nginx ở Step 1, các kết nối kết thúc tại nginx trên host và đi qua chain INPUT. Khi đó, ban bằng DOCKER-USER sẽ không thấy các kết nối này. Hãy xóa dòng chain = DOCKER-USER để Fail2ban dùng chain mặc định INPUT.
Thứ ba, hãy dùng banaction = iptables-allports thay cho mặc định dựa trên port. Jail này không định nghĩa port. Ban toàn bộ port trong DOCKER-USER sẽ chặn offender khỏi mọi service được publish trên máy.
Bước 8, sao lưu vault, rồi thực sự khôi phục nó
Volume vw-data chính là password manager của bạn. Nó chứa db.sqlite3 (toàn bộ entry), các thư mục attachments/ và sends/, các file rsa_key.* dùng để ký login session, cùng config.json từ admin panel. Bản sao lưu bỏ sót bất kỳ thành phần nào trong số này sẽ thất bại khi bạn cần khôi phục.
Sao chép db.sqlite3 trong lúc Vaultwarden đang ghi có thể tạo ra file được ghi dở và bị hỏng. Vì vậy, hãy tạo cold snapshot; thời gian downtime chỉ vài giây:
#!/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 vaultwardenChạy việc này bằng cron mỗi đêm và sao chép .tgz ra khỏi máy. Bản sao lưu chỉ nằm trên chính server mà bạn đang bảo vệ thì không phải là bản sao lưu. Cách chuyển bản sao lưu phù hợp là dùng restic sao lưu hằng đêm sang server khác hoặc object storage, công cụ này mã hóa archive và deduplicate các snapshot lặp lại cho bạn. Nút Backup Database trong admin panel là cách thuận tiện để tạo snapshot nóng chỉ của file SQLite, nhưng nó không bao gồm attachment và key.
Bây giờ là bước phân biệt bản sao lưu thực sự với bản sao lưu chỉ mang tính hy vọng: hãy khôi phục một lần và xác nhận nó hoạt động:
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/serverTừ laptop, hãy tạo tunnel đến đó bằng ssh -L 8888:127.0.0.1:8888 you@your-vps và mở http://localhost:8888 (hoặc http://127.0.0.1:8888). Vì địa chỉ loopback được xem là secure context, crypto.subtle khả dụng và vault có thể giải mã qua plain http ở đây; đây là loại địa chỉ duy nhất cho phép cách này. Đăng nhập bằng master password và xác nhận các entry của bạn vẫn còn. Nếu có, database, RSA key và master password của bạn đều đã được khôi phục đầy đủ, và bạn có thể dựng lại trên một VPS mới trong vài phút. Dừng container bằng Ctrl-C rồi xóa /tmp/vw-restore. Hãy giữ thói quen dùng tunnel này cho mọi admin UI khác trên máy không bao giờ được public Internet. Đây cũng là cách bạn truy cập open-kritt security scanner tự host trên port 5173.
Các lỗi thường gặp và chuỗi bạn sẽ thấy
Cannot read properties of undefined (reading 'importKey') trong browser console. Vault được tải qua http nên crypto.subtle không được định nghĩa; chỉ truy cập vault qua https:// và thêm redirect từ HTTP sang HTTPS tại proxy.
This is not a recognized Bitwarden server... trong client. Server URL dùng http, bị nhập sai hoặc certificate không được tin cậy; xác nhận https://vault.example.com hiển thị biểu tượng ổ khóa hợp lệ, rồi nhập lại URL đó trong phần self-hosted settings của client.
/admin từ chối password chính xác. Argon2 hash bị mất ký tự escape; mọi $ phải là $$ trong Compose file (nhưng chỉ một $ bên trong chuỗi được đặt trong dấu nháy đơn tại .env), hoặc bạn đã nhập hash thay vì plaintext mà hash đó biểu diễn.
Đồng bộ chậm giữa các thiết bị; console hiển thị WebSocket connection to 'wss://vault.example.com/notifications/hub' failed. Proxy không chuyển tiếp các header Upgrade/Connection; Traefik tự xử lý việc này, còn nginx cần 2 dòng upgrade trong Step 1. Vault vẫn hoạt động, nhưng chỉ đồng bộ khi mở. Port riêng 3012 cũ đã bị loại bỏ từ v1.31.0, nên không cần route WebSocket riêng.
Fail2ban báo đã ban nhưng attacker vẫn tiếp tục kết nối. Nó đang ban 127.0.0.1 vì IP_HEADER không đúng, hoặc rule ban nằm trong iptables chain sai; hãy đặt chain = DOCKER-USER và banaction = iptables-allports.
Nâng cấp
Pull image mới và tạo lại container; named volume cùng toàn bộ dữ liệu vẫn được giữ nguyên:
docker compose pull
docker compose up -dVaultwarden phát hành bản mới thường xuyên. Hãy theo dõi ghi chú phát hành của project thay vì pin một patch version, vì một số bản phát hành có kèm hướng dẫn migration. Hãy tạo backup mới trước mỗi lần nâng cấp lớn; bạn có thể rollback bằng cách khôi phục tarball vào một volume mới.
FAQ
Vaultwarden có giống Bitwarden không?
Đây là một server độc lập tương thích với Bitwarden, không phải server chính thức. Vaultwarden triển khai lại Bitwarden server API bằng Rust, nên các client chính thức trên desktop, mobile, browser và CLI đều hoạt động với nó, trong khi chỉ dùng một phần nhỏ tài nguyên so với stack chính thức. Định dạng vault giống nhau, nên bạn có thể migrate theo cả hai chiều bằng cách export và import.
Tôi có thực sự cần HTTPS không, hay có thể chạy bằng http trong LAN?
Bạn cần HTTPS cho mọi trường hợp ngoài kiểm thử loopback. Web vault và extension của Bitwarden dùng Web Crypto API của browser. API này chỉ hoạt động trong secure context, nên client sẽ báo Cannot read properties of undefined và không thể đăng nhập khi dùng http thuần. Các địa chỉ http duy nhất hoạt động là địa chỉ loopback như http://localhost hoặc http://127.0.0.1. Vì vậy, bài kiểm tra restore ở Step 8 dùng SSH tunnel.
Làm cách nào để ngăn người lạ đăng ký trên server của tôi?
Đặt SIGNUPS_ALLOWED: "false" trong Compose file rồi chạy docker compose up -d ngay sau khi tạo account của bạn. Từ đó, thêm người dùng mới bằng nút Invite User trong /admin. Tính năng này cần SMTP được cấu hình để họ nhận link mời. Thỉnh thoảng kiểm tra danh sách admin user để xác nhận không có account lạ xuất hiện.
Làm cách nào để backup vault Vaultwarden?
Tạm dừng container rồi archive toàn bộ volume vw-data, db.sqlite3, attachments/, sends/, config.json và các file rsa_key.*. Sau đó copy archive ra ngoài server, tốt nhất là chạy bằng cron mỗi đêm. Copy file SQLite khi server đang chạy có thể tạo snapshot bị corrupt, nên hãy backup khi server đã dừng. Quan trọng nhất, hãy restore một lần vào một container dùng để thử nghiệm rồi đăng nhập, để xác nhận backup thực sự dùng được trước khi phụ thuộc vào nó.
Tự host password có thực sự an toàn không?
Có, nếu bạn thực hiện 3 việc được nêu trong hướng dẫn này: dùng HTTPS thực sự, tắt đăng ký tự do và dùng admin token mạnh, đồng thời kiểm thử backup. Vault được mã hóa ở phía client bằng master password của bạn, nên ngay cả server cũng không thấy password ở dạng rõ. Một db.sqlite3 bị đánh cắp cũng không làm lộ các entry nếu không có master password. Tuy nhiên, server vẫn lưu email của account và salted hash được tạo từ từng master password. Kẻ tấn công có thể thử crack chúng offline, nên hãy bảo vệ file database và mọi bản sao của file đó. Đổi lại, việc patch và backup trở thành trách nhiệm của bạn. Vì vậy, Fail2ban và quy trình restore không phải tùy chọn trong hướng dẫn này. Khi đã hoàn tất các phần đó, xem kỹ hơn những nơi vault tự host thực sự có thể bị tấn công là bước tiếp theo hữu ích. Vì các entry đã được mã hóa ở phía client, phần còn lại cần bảo vệ là admin token và backup archive.