Self-Host Vaultwarden + Traefik + Cloudflare Tunnel

6/5/2025

๐Ÿงญ Overview

This is a walkthrough of my secure, private, self-hosted password manager setup using:

  • Vaultwarden (Bitwarden-compatible Rust rewrite)
  • Docker
  • Traefik as a reverse proxy
  • Cloudflare Tunnel for remote HTTPS access
  • Tailscale for internal LAN access

๐Ÿ› ๏ธ My Setup

ComponentRole
VaultwardenPassword manager backend
TraefikReverse proxy & TLS termination
Cloudflare TunnelExposes Vaultwarden securely without port-forwarding
Docker ComposeOrchestration layer
TailscaleVPN mesh for local/private access

๐Ÿงฑ Folder Structure

~/docker/vaultwarden/
โ”œโ”€โ”€ docker-compose.yml
โ”œโ”€โ”€ traefik_dynamic.yml
โ”œโ”€โ”€ .env
โ””โ”€โ”€ data/

โš™๏ธ docker-compose.yml

version: "3.8"

services:
  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    environment:
      - WEBSOCKET_ENABLED=true
    env_file:
      - .env
    volumes:
      - ./data:/data
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.vaultwarden.rule=Host(`vaultwarden.neilpatterson.dev`)"
      - "traefik.http.routers.vaultwarden.entrypoints=websecure"
      - "traefik.http.routers.vaultwarden.tls.certresolver=cloudflare"
      - "traefik.http.services.vaultwarden.loadbalancer.server.port=80"
    networks:
      - web

networks:
  web:
    external: true

๐Ÿ” .env File

# Vaultwarden admin panel (optional)
ADMIN_TOKEN=supersecureadminkey

# Encryption key (auto-generated on first run or provide your own)
DATABASE_URL=data/db.sqlite3

Restrict file permissions:

chmod 600 .env

๐ŸŒ DNS & Cloudflare Setup

  1. Add a DNS record in Cloudflare:
  • Type: CNAME
  • Name: vaultwarden
  • Target: your-cloudflare-tunnel-id.cfargotunnel.com
  • Proxy: โœ… enabled
  1. Set up Cloudflare Tunnel:
cloudflared tunnel create vaultwarden-tunnel
cloudflared tunnel route dns vaultwarden-tunnel vaultwarden.neilpatterson.dev
  1. Define config in /etc/cloudflared/config.yml:
tunnel: vaultwarden-tunnel
credentials-file: /etc/cloudflared/vaultwarden-tunnel.json

ingress:
  - hostname: vaultwarden.neilpatterson.dev
    service: http://localhost:80
  - service: http_status:404
  1. Start the tunnel:
sudo systemctl start cloudflared

๐Ÿ” Access Over Tailscale

  • You can also access Vaultwarden via local Tailscale IP:
http://100.x.y.z:PORT
  • Useful for maintenance or as a backup path if Cloudflare Tunnel goes down.

โœ… Security Notes

  • Vaultwarden encrypts all data client-side.
  • Traefik terminates TLS using Let's Encrypt (or Cloudflare DNS challenge).
  • Cloudflare Tunnel eliminates port-forwarding and public IP exposure.
  • Admin panel protected via ADMIN_TOKEN env var.

๐Ÿงช Restore & Backup

Backups

I backup the /data folder with Restic:

restic backup ~/docker/vaultwarden/data

Restore

  • Just copy db.sqlite3 and config JSON to a fresh container

๐Ÿ’ก Final Thoughts

This setup has been stable, private, and cost-effective. I get:

  • Browser extensions & mobile app support
  • No recurring fees
  • Full control of my data
  • Local + remote access