Vaultwarden is a password manager with cross-platform support. The web version adds a Chrome plugin, and iOS can set Vaultwarden for Auto Fill in Password settings. I previously used iOS’s built-in password manager and Safari on mobile, so I had to re-save passwords on my computer. Fearing forgetfulness, I set all passwords the same, a huge security risk.

Password managers are best self-hosted. Using official services differs little from iOS/Chrome. This article covers deploying Vaultwarden to a mini PC with Docker Compose and Cloudflared.

A reminder: managing passwords yourself is tedious. While security improves greatly, effort increases proportionally. Later password export/import and data backup become significant issues.

Deployment

I tried two deployment methods: Nginx and Cloudflared, each with pros and cons. This article mainly covers adding services to their networks. For pre-setup, see:

Nginx

  • First, configure Nginx.

  • Create a folder for storing configuration and Docker data:

    ~
    β”œβ”€β”€ nginx
    └── vaultwarden
        β”œβ”€β”€ docker-compose.yml
        └── server
    
  • Enter vaultwarden and create docker-compose.yml:

    services:
      vaultwarden:
        image: vaultwarden/server:latest
        container_name: vaultwarden
        restart: unless-stopped
    
        expose:
          - "80"
    
        environment:
          DOMAIN: "https://your.domain.com"
          VIRTUAL_HOST: "your.domain.com"
          LETSENCRYPT_HOST: "your.domain.com"
          LETSENCRYPT_EMAIL: "[email protected]"
          ICON_SERVICE: "bitwarden"
          DISABLE_ICON_DOWNLOAD: "true"
          ADMIN_TOKEN: 'your_admin_token'
          IP_HEADER: "X-Forwarded-For"
          SIGNUPS_ALLOWED: "false"					# Disable other user signups by default
    
        volumes:
          - ./data:/data
    
        networks:
          - nginx
    
    networks:
      nginx:
        external: true
    

    The ADMIN_TOKEN is custom. Use a long string for better security. It’s recommended to create a plaintext password, convert it to a hash using argon2, and use the hash as ADMIN_TOKEN. Below is a command to pull Alpine, install argon2, generate a token, replace $ with $$, then delete the container:

    docker run --rm alpine sh -c 'apk add --no-cache argon2 openssl >/dev/null && echo -n "YOUR SECRET" | argon2 $(openssl rand -base64 32) -e -id -k 19456 -t 2 -p 1 | sed "s/\\$/\$\$/g"'
    

    Note:

    • Don’t forget the plaintext password.
    • After doubling $$, use single quotes in docker-compose.yml.
  • Start the container:

    docker compose up -d
    
  • After registering the domain and opening router port 443, you can access publicly.

Cloudflared

  • First, configure Cloudflared.

  • Create a folder for configuration and Docker data:

    ~
    β”œβ”€β”€ cloudflared
    └── vaultwarden
        β”œβ”€β”€ docker-compose.yml
        └── server
    
  • Enter vaultwarden and create docker-compose.yml. Everything is the same except change the network to Cloudflared (after creating the cloudflared Docker network and having the Cloudflared container join it):

    services:
      vaultwarden:
        image: vaultwarden/server:latest
        container_name: vaultwarden
        restart: unless-stopped
    
        expose:
          - "80"
    
        environment:
          DOMAIN: "https://your.domain.com"
          VIRTUAL_HOST: "your.domain.com"
          LETSENCRYPT_HOST: "your.domain.com"
          LETSENCRYPT_EMAIL: "[email protected]"
          ICON_SERVICE: "bitwarden"
          DISABLE_ICON_DOWNLOAD: "true"
          ADMIN_TOKEN: 'your_admin_token'
          IP_HEADER: "X-Forwarded-For"
          SIGNUPS_ALLOWED: "false"					# Disable other user signups by default
    
        volumes:
          - ./data:/data
    
        networks:
          - cloudflared
    
    networks:
      cloudflared:
        external: true
    
  • Note that your Cloudflared web config should use the same port as your local Docker container.

  • After starting, you can login to the admin page at your_hostname/admin. Enter the plaintext password:

    Admin Login

  • But it’s better to configure with docker-compose.yml as it has higher priority and avoids conflicts. Use the admin page only for viewing.

  • Add SMTP and email configuration. I use Gmail:

    environment:
      SMTP_HOST: "smtp.gmail.com"
      SMTP_FROM: "YOUR EMAIL"
      SMTP_FROM_NAME: "Vaultwarden"
      SMTP_SECURITY: "starttls"
      SMTP_PORT: "587"
      SMTP_USERNAME: "YOUR EMAIL"
      SMTP_PASSWORD: "YOUR PASSWORD"
      SMTP_AUTH_MECHANISM: "Plain"
      SMTP_TIMEOUT: "15"
    

    After filling in, restart the container.

  • You can then go to the Users page and invite new users, including yourself.