# Self host password manager with Vaultwarden and Traefik

[https://blog.puvvadi.me/posts/selfhost-paswword-manager-vaultwarden-traefik/](https://blog.puvvadi.me/posts/selfhost-paswword-manager-vaultwarden-traefik/)

[https://tech.aufomm.com/deploy-bitwarden-with-docker-and-traefik/](https://tech.aufomm.com/deploy-bitwarden-with-docker-and-traefik/)

Vaultwarden is light weight feature rich drop in replacement for Bitwarden server. It’s essentially debloated version of the Bitwarden.

To use Vaultwarden, SSL is required. Otherwise, singing in to the server is impossible. That’s where traefik comes in. Here I’m assuming you have a domain, cloudflare account &amp; domain added to your account, and docker is already set up and ready to go.

## <span class="me-2">Cloudflare</span>

Please keep cloudflare’s `Email`, `API KEY` or `API TOKEN` ready.

## <span class="me-2">Traefik setup</span>

Create required files

### <span class="me-2">directory structure</span>

<table class="rouge-table" id="bkmrk-%C2%A0"><tbody><tr><td class="rouge-gutter gl"> </td></tr></tbody></table>

<div class="language-shell highlighter-rouge" id="bkmrk-touch-docker-compose"><div class="highlight"><table class="rouge-table"><tbody><tr><td class="rouge-code">```
touch docker-compose.yml
touch config.yml
mkdir data && cd data
touch acme.json
touch traefik.yml
```

</td></tr></tbody></table>

</div></div>Directory structure should be like this

<div class="language-shell highlighter-rouge" id="bkmrk-">  
</div><table class="rouge-table" id="bkmrk-%C2%A0-1"><tbody><tr><td class="rouge-gutter gl"> </td></tr></tbody></table>

<div class="language-shell highlighter-rouge" id="bkmrk-%7C%E2%94%80%E2%94%80-docker-compose.y"><div class="highlight"><table class="rouge-table"><tbody><tr><td class="rouge-code">```
|── docker-compose.yml
├── config.yml
└── data
    ├── acme.json
    └── traefik.yml
```

</td></tr></tbody></table>

</div></div>### <span class="me-2">Docker network</span>

Create a network with following

<div class="language-shell highlighter-rouge" id="bkmrk--1">  
</div><table class="rouge-table" id="bkmrk-%C2%A0-2"><tbody><tr><td class="rouge-gutter gl"> </td></tr></tbody></table>

<div class="language-shell highlighter-rouge" id="bkmrk-docker-network-creat"><div class="highlight"><table class="rouge-table"><tbody><tr><td class="rouge-code">```
docker network create -d bridge proxy
```

</td></tr></tbody></table>

</div></div>### <span class="me-2">Docker compose</span>

First open `docker-compose.yml` and add following

<div class="language-yaml highlighter-rouge" file="docker-compose.yml" id="bkmrk--2">  
</div><table class="rouge-table" id="bkmrk-%C2%A0-3"><tbody><tr><td class="rouge-gutter gl"> </td></tr></tbody></table>

<div class="language-yaml highlighter-rouge" file="docker-compose.yml" id="bkmrk-version%3A-%273%27-service"><div class="highlight"><table class="rouge-table"><tbody><tr><td class="rouge-code">```
version: '3'

services:
  traefik:
    image: traefik:latest
    container_name: traefik
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    ports:
      - 80:80
      - 443:443
    dns:
      - 1.1.1.1
      - 8.8.8.8
    environment:
      - CF_API_EMAIL=email@example.com
    # - CF_API_KEY= # use either api key or api token based on you usecase
    #   - CF_API_TOKEN=
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /home/user/traefik/data/traefik.yml:/traefik.yml:ro
      - /home/user/traefik/data/acme.json:/acme.json
      - /home/user/traefik/config.yml:/config.yml:ro
    labels:
      - "traefik.enable=true"
      # http entrypoint
      - "traefik.http.routers.traefik.entrypoints=http"
      # Dashboard
      - "traefik.http.routers.traefik.rule=Host(`traefik.internal.example.net`)"
      # To create a user:password pair, the following command can be used:
      # echo $(htpasswd -nb user password) | sed -e s/\\$/\\$\\$/g
      - "traefik.http.middlewares.traefik-auth.basicauth.users=<user & password>"
      # redirect middleware
      - "traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.middlewares.sslheader.headers.customrequestheaders.X-Forwarded-Proto=https"
      - "traefik.http.routers.traefik.middlewares=traefik-https-redirect"
      # https entrypoint
      - "traefik.http.routers.traefik-secure.entrypoints=https"
      - "traefik.http.routers.traefik-secure.rule=Host(`traefik.internal.example.net`)"
      - "traefik.http.routers.traefik-secure.middlewares=traefik-auth"
      - "traefik.http.routers.traefik-secure.tls=true"
      - "traefik.http.routers.traefik-secure.tls.certresolver=cloudflare"
      - "traefik.http.routers.traefik-secure.tls.domains[0].main=internal.example.net"
      - "traefik.http.routers.traefik-secure.tls.domains[0].sans=*.internal.example.net"
      - "traefik.http.routers.traefik-secure.service=api@internal"

networks:
  proxy:
    external: true
```

</td></tr></tbody></table>

</div></div>> dns records should already pointed to you docker host. e.g. if docker host ip is `10.20.20.5` A record for `traeif.internal` should point to `10.20.20.5`.

### <span class="me-2">traefik config</span>

<table class="rouge-table" id="bkmrk-%C2%A0-4"><tbody><tr><td class="rouge-gutter gl"> </td></tr></tbody></table>

<div class="language-yml highlighter-rouge" file="data/traefik.yml" id="bkmrk-api%3A-dashboard%3A-true"><div class="highlight"><table class="rouge-table"><tbody><tr><td class="rouge-code">```
api:
  dashboard: true
  debug: true
entryPoints:
  http:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: https
          scheme: https
  https:
    address: ":443"
serversTransport:
  insecureSkipVerify: true
providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
  file:
    filename: /config.yml
certificatesResolvers:
  cloudflare:
    acme:
      email: email@example.net
      storage: acme.json
      dnsChallenge:
        provider: cloudflare
        disablePropagationCheck: true
        resolvers:
          - "1.1.1.1:53"
          - "1.0.0.1:53"
```

</td></tr></tbody></table>

</div></div>To spin up the traefik docker container, run

<div class="language-shell highlighter-rouge" id="bkmrk--3">  
</div><table class="rouge-table" id="bkmrk-%C2%A0-5"><tbody><tr><td class="rouge-gutter gl"> </td></tr></tbody></table>

<div class="language-shell highlighter-rouge" id="bkmrk-docker-compose-up--d"><div class="highlight"><table class="rouge-table"><tbody><tr><td class="rouge-code">```
docker-compose up -d
```

</td></tr></tbody></table>

</div></div>Once docker container created, traefik will generate ssl certs for `internel.example.net` &amp; wildcard cert for `*.internel.example.net`. traefik dashboard will be available at `traefik.internal.example.net`.

## <span class="me-2">Vaultwarden</span>

### <span class="me-2">Volume</span>

I’m using named volumes here for the sake. You can use any directory on the host and bind that.

<div class="language-shell highlighter-rouge" id="bkmrk--4">  
</div><table class="rouge-table" id="bkmrk-%C2%A0-6"><tbody><tr><td class="rouge-gutter gl"> </td></tr></tbody></table>

<div class="language-shell highlighter-rouge" id="bkmrk-docker-volume-create"><div class="highlight"><table class="rouge-table"><tbody><tr><td class="rouge-code">```
docker volume create vaultwarden
```

</td></tr></tbody></table>

</div></div>Reason for creating volume outside the compose file, in case, container destroyed with `rm`, data would be still available from the volume.

### <span class="me-2">Docker-compose</span>

Create a new directory in your home `vaultwarden` and add new file `docker-compose.yml`.

<div class="language-yml highlighter-rouge" id="bkmrk--5">  
</div><table class="rouge-table" id="bkmrk-%C2%A0-7"><tbody><tr><td class="rouge-gutter gl"> </td></tr></tbody></table>

<div class="language-yml highlighter-rouge" id="bkmrk-version%3A-%273%27-service-1"><div class="highlight"><table class="rouge-table"><tbody><tr><td class="rouge-code">```
version: '3'

services:
  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    ports:
      - 8100:80
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - vaultwarden:/data
    environment:
      - DOMAIN=https://vaultwarden.internal.example.net
      - SMTP_HOST=smtp.example.com
      - SMTP_FROM=email@example.com
      - SMTP_FROM_NAME=Vaultwarden
      - SMTP_SECURITY=SECURITYMETHOD
      - SMTP_PORT=XXXX
      - SMTP_USERNAME=email@example.com
      - SMTP_PASSWORD=YourReallyStrongPasswordHere
      - SMTP_AUTH_MECHANISM="Mechanism"
    labels:
      - traefik.enable=true
      - traefik.docker.network=proxy
      - traefik.http.middlewares.redirect-https.redirectScheme.scheme=https
      - traefik.http.middlewares.redirect-https.redirectScheme.permanent=true
      - traefik.http.routers.vaultwarden-https.rule=Host(`vaultwarden.internal.example.net`)
      - traefik.http.routers.vaultwarden-https.entrypoints=https
      - traefik.http.routers.vaultwarden-https.tls=true
      - traefik.http.routers.vaultwarden-https.service=vaultwarden
      - traefik.http.routers.vaultwarden-http.rule=Host(`vaultwarden.internal.example.net`)
      - traefik.http.routers.vaultwarden-http.entrypoints=http
      - traefik.http.routers.vaultwarden-http.middlewares=redirect-https
      - traefik.http.routers.vaultwarden-http.service=vaultwarden
      - traefik.http.services.vaultwarden.loadbalancer.server.port=80
      - traefik.http.routers.vaultwarden-websocket-https.rule=Host(`vaultwarden.internal.example.net`) && Path(`/notifications/hub`)
      - traefik.http.routers.vaultwarden-websocket-https.entrypoints=https
      - traefik.http.routers.vaultwarden-websocket-https.tls=true
      - traefik.http.routers.vaultwarden-websocket-https.service=vaultwarden-websocket
      - traefik.http.routers.vaultwarden-websocket-http.rule=Host(`vaultwarden.internal.example.net`) && Path(`/notifications/hub`)
      - traefik.http.routers.vaultwarden-websocket-http.entrypoints=http
      - traefik.http.routers.vaultwarden-websocket-http.middlewares=redirect-https
      - traefik.http.routers.vaultwarden-websocket-http.service=vaultwarden-websocket
      - traefik.http.services.vaultwarden-websocket.loadbalancer.server.port=3012

networks:
  proxy:
    external: true

volumes:
  vaultwarden:
    external: true
```

</td></tr></tbody></table>

</div></div>> dns records should already pointed to you docker host. e.g. if docker host ip is `10.20.20.5` A record for `vaultwarden.internal` should point to `10.20.20.5`.

To spin up the vaultwarden docker container, run

<div class="language-shell highlighter-rouge" id="bkmrk--6">  
</div><table class="rouge-table" id="bkmrk-%C2%A0-8"><tbody><tr><td class="rouge-gutter gl"> </td></tr></tbody></table>

<div class="language-shell highlighter-rouge" id="bkmrk-docker-compose-up--d-1"><div class="highlight"><table class="rouge-table"><tbody><tr><td class="rouge-code">```
docker-compose up -d
```

</td></tr></tbody></table>

</div></div>## <span class="me-2">Conclusion</span>

For more details and documentation, visit Official [github](https://github.com/dani-garcia/vaultwarden) repo. Any queries, feel free to drop a comment. `Au Revoir`.