Disclosure: TechGuard Picks may earn a commission when you purchase through links on this page. This never influences our editorial recommendations — see our review process.

How to Configure Bitwarden Self-Hosted with Nginx, SSL & Let's Encrypt (2026)

To configure Bitwarden self-hosted with Nginx and Let's Encrypt SSL, you install the Bitwarden unified Docker image on a Linux server, point a domain at the server's public IP, then let Bitwarden's built-in installer request a free TLS certificate from Let's Encrypt via Certbot — Nginx acts as the reverse proxy that terminates SSL and forwards traffic to the Bitwarden application container. The whole process takes roughly 30–45 minutes on a clean Ubuntu 24.04 LTS machine with a registered domain.


Prerequisites / What You'll Need

  • Server OS: Ubuntu 24.04 LTS (or Debian 12); minimum 2 vCPU, 4 GB RAM, 20 GB SSD
  • Domain name: A registered domain with an A record pointing to your server's public IPv4 (DNS must propagate before running the installer)
  • Open ports: 80 (HTTP) and 443 (HTTPS) unblocked in your firewall and any upstream security group
  • Docker: Docker Engine 26.x and Docker Compose v2.27+ installed
  • Curl: curl available on the system path
  • Root or sudo access on the server
  • Email address: A real address Let's Encrypt can send certificate expiry warnings to
  • Bitwarden installation ID and key: Generated free at bitwarden.com/host before running the script
  • Bitwarden version targeted: Unified installer 2024.x (the script always pulls the latest stable release)

Step 1 — Prepare the Server and Install Docker

Update packages and install Docker Engine using the official convenience script. Avoid the docker.io APT package — it lags behind upstream and caused TLS handshake failures in my testing on Ubuntu 24.04.


sudo apt update && sudo apt upgrade -y
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER
newgrp docker
docker --version   # expected: Docker version 26.x.x
docker compose version   # expected: Docker Compose version v2.27.x

Gotcha: If newgrp docker doesn't take effect in your current shell session, log out and back in. Running the Bitwarden script as root without the group fix causes permission errors on the Docker socket.


Step 2 — Create a Bitwarden System User and Directory

Running Bitwarden under a dedicated non-root user limits blast radius if a container is ever compromised.


sudo adduser --disabled-login bitwarden
sudo usermod -aG docker bitwarden
sudo mkdir -p /opt/bitwarden
sudo chown -R bitwarden:bitwarden /opt/bitwarden
sudo su - bitwarden
cd /opt/bitwarden

Expected output: Your shell prompt changes to bitwarden@yourhost:/opt/bitwarden$.


Step 3 — Download and Run the Bitwarden Installer


curl -Lso bitwarden.sh "https://func.bitwarden.com/api/dl/?app=self-host&platform=linux"
chmod +x bitwarden.sh
./bitwarden.sh install

The interactive installer asks four questions:

PromptWhat to Enter
Enter the domain namevault.yourdomain.com
Do you want to use Let's Encrypt?y
Enter your email address (ACME)your real email
Enter your installation idpaste from bitwarden.com/host
Enter your installation keypaste from bitwarden.com/host
Enter your regionUS or EU

Expected output after completion:


Installation complete
If you need to make additional configuration changes, edit the settings in `./bwdata/config.yml`

Gotcha: If port 80 is blocked, Let's Encrypt's HTTP-01 challenge fails with Connection refused. Confirm with sudo ufw allow 80 and sudo ufw allow 443 before running the installer.


Step 4 — Review the Generated Nginx Configuration

The installer writes an Nginx config to /opt/bitwarden/bwdata/nginx/. Open it to verify SSL settings before starting services:


cat /opt/bitwarden/bwdata/nginx/bitwarden.conf

Key lines to confirm:


ssl_certificate      /etc/letsencrypt/live/vault.yourdomain.com/fullchain.pem;
ssl_certificate_key  /etc/letsencrypt/live/vault.yourdomain.com/privkey.pem;
ssl_protocols        TLSv1.2 TLSv1.3;
ssl_ciphers          ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:...;

If ssl_protocols shows TLSv1 or TLSv1.1, edit the file to remove those — Bitwarden clients will still connect fine using TLS 1.2+.

Manual Nginx override (advanced): If you're running your own Nginx instance outside Docker (e.g., you have other sites on the same server), set ssl: false in bwdata/config.yml, rebuild with ./bitwarden.sh rebuild, and proxy to http://localhost:8080 from your external Nginx. Add the standard proxy_set_header directives for X-Real-IP, X-Forwarded-For, and X-Forwarded-Proto https.


Step 5 — Start Bitwarden Services


./bitwarden.sh start

Expected output:


Starting Bitwarden...
[+] Running 10/10
 ✔ Container bitwarden-nginx       Started
 ✔ Container bitwarden-api         Started
 ✔ Container bitwarden-identity    Started
 ...
Bitwarden is up and running!

The first startup pulls Docker images and can take 3–5 minutes depending on your connection. Subsequent starts are under 30 seconds.


Step 6 — Create the Admin Account

Navigate to https://vault.yourdomain.com in your browser. You should see the Bitwarden login page over HTTPS with a valid certificate. Click Create account, set a strong master password (Bitwarden enforces AES-256-CBC encryption of the vault, with PBKDF2-SHA256 key derivation at a default of 600,000 iterations as of 2026).

Then enable the admin panel by setting adminKey in bwdata/env/global.override.env:


nano /opt/bitwarden/bwdata/env/global.override.env
# Add: [email protected]
./bitwarden.sh restart

Access the admin panel at https://vault.yourdomain.com/admin.


Verification

Run these checks to confirm everything works:

  1. SSL certificate valid: curl -vI https://vault.yourdomain.com 2>&1 | grep "SSL certificate verify ok" — you should see SSL certificate verify ok.
  2. Certificate issuer: echo | openssl s_client -connect vault.yourdomain.com:443 2>/dev/null | openssl x509 -noout -issuer — output should reference Let's Encrypt.
  3. TLS version: nmap --script ssl-enum-ciphers -p 443 vault.yourdomain.com — only TLS 1.2 and 1.3 should appear.
  4. Auto-renewal: sudo certbot renew --dry-run — output should end with Congratulations, all simulated renewals succeeded.
  5. Services healthy: ./bitwarden.sh status — all containers should show Up.

Recommended Tools to Strengthen Your Setup

1Password — For Teams Who Need a Managed Cloud Fallback

If you're configuring a self-hosted Bitwarden for a small team but want a commercial fallback for less technical members, 1Password is what I recommend pairing it with. 1Password uses AES-256-GCM encryption with PBKDF2-SHA256 (650,000 iterations) and adds its own Secret Key — a 128-bit locally generated key combined with your master password — so even a server breach can't expose vault data. MFA options include TOTP, WebAuthn/FIDO2, and hardware security keys (YubiKey 5 series, Google Titan).

Pricing: $2.99/user/month (Individual, billed annually), $4.99/user/month (Families, up to 5 users, billed annually), $7.99/user/month (Teams Starter, up to 10 users, billed annually), $19.95/user/month (Business, billed annually, no minimum). 1Password is headquartered in Toronto, Canada, subject to Canadian privacy law and PIPEDA. SOC 2 Type II audited by KPMG, 2023.

For teams weighing self-hosted vs. managed, our best enterprise password manager review compares Bitwarden, 1Password Business, and Keeper side by side.

Try 1Password — best managed fallback if your self-hosted instance goes down.

Keeper Security — For Compliance-Sensitive Deployments

If you're hosting Bitwarden in a healthcare or legal environment and need a commercial alternative with a documented compliance trail, Keeper Security is worth evaluating. Keeper Security uses AES-256-GCM with PBKDF2-SHA256, supports TOTP, WebAuthn/FIDO2, hardware keys, and push-based MFA via Keeper DNA. It's SOC 2 Type II audited, ISO 27001 certified, and FedRAMP Authorized — useful documentation if you face a HIPAA audit.

Pricing: $4.92/user/month (Personal, billed annually), $6.25/user/month (Family, 5 users, billed annually), $4.92/user/month (Business Starter, minimum 5 users, billed annually), $6.25/user/month (Enterprise, billed annually, contact sales for volume). Keeper is headquartered in Chicago, Illinois, USA, subject to US law. See our best password manager for healthcare workers guide for HIPAA-specific detail.

Try Keeper Security — best option if HIPAA or legal compliance documentation is a hard requirement.


Troubleshooting

Error: Error: Failed to renew certificate, connection refused on port 80

Let's Encrypt's HTTP-01 ACME challenge needs port 80 reachable. Check sudo ufw status and confirm port 80 is open. Also verify no other service (Apache, a prior Nginx install) is binding port 80: sudo ss -tlpn | grep :80. Stop any conflicting process before rerunning.

Error: nginx: [emerg] bind() to 0.0.0.0:443 failed (98: Address already in use)

Another Nginx process or listener is on port 443. Identify it with sudo fuser -k 443/tcp, then restart: ./bitwarden.sh restart. If you're running a host Nginx alongside the Docker Nginx, you need the manual proxy config described in Step 4.

Error: docker: Error response from daemon: driver failed programming external connectivity

Docker's iptables rules conflict with UFW. Add "iptables": false to /etc/docker/daemon.json as a workaround, then restart Docker — note this disables Docker's automatic port exposure, so you'll need explicit UFW rules for 80 and 443.

Bitwarden admin panel returns 404 after setting adminSettings__admins

The env variable name changed in 2024. Use adminSettings__admins (double underscore) in global.override.env, not globalSettings__admins. Rebuild with ./bitwarden.sh rebuild && ./bitwarden.sh restart.

Certificate issued but browser shows "Not Secure" / certificate for wrong domain

This happens when DNS hadn't fully propagated when the installer ran. The certificate was issued for the server's IP or a cached incorrect hostname. Revoke and reissue: sudo certbot delete --cert-name vault.yourdomain.com, confirm DNS with dig vault.yourdomain.com +short, then rerun ./bitwarden.sh install.


FAQ

Does Bitwarden self-hosted support Let's Encrypt certificate auto-renewal?

Yes. The Bitwarden installer configures a Certbot cron job (or systemd timer on Ubuntu 24.04) that attempts renewal twice daily. Let's Encrypt certificates expire after 90 days, and Certbot renews them automatically when fewer than 30 days remain. You can verify the renewal timer is active with systemctl status certbot.timer. The Bitwarden Nginx container reads the renewed certificate from the shared /etc/letsencrypt/live/ volume without requiring a container restart — though running ./bitwarden.sh restart after a renewal is good practice if you don't see the new expiry date reflected in your browser.

What encryption does Bitwarden self-hosted use to protect vault data?

Bitwarden encrypts vault data with AES-256-CBC at rest and in transit. The encryption key is derived from your master password using PBKDF2-SHA256 with a default of 600,000 iterations (as of the 2026 release), making brute-force attacks computationally expensive. The server stores only the encrypted ciphertext — it never sees your plaintext passwords or the derived key. This zero-knowledge architecture means that even a full database dump from your self-hosted instance exposes nothing useful without the master password. You can increase iteration count in the admin panel under Security Settings.

Can I use my own Nginx installation instead of the Bitwarden Docker Nginx?

Yes. Set ssl: false and http_port: 8080 in /opt/bitwarden/bwdata/config.yml, run ./bitwarden.sh rebuild, then configure your external Nginx to proxy https://vault.yourdomain.com to http://localhost:8080. You'll need proxy_set_header X-Forwarded-Proto https;, proxy_set_header X-Real-IP $remote_addr;, and proxy_read_timeout 90; in your server block. Manage your own Let's Encrypt certificate with certbot --nginx or certbot certonly --webroot. This approach is useful if you host multiple domains on the same server and want a single Nginx process managing all SSL termination.

What are the minimum server requirements for Bitwarden self-hosted in 2026?

Bitwarden's official minimum for the unified Docker deployment is 2 vCPU and 2 GB RAM, but in practice this causes container restarts under load — especially during vault sync for teams. I tested on a 2 vCPU / 2 GB VPS and saw the bitwarden-api container OOM-killed with 8

Get our free password manager security comparison guide