This post may contain affiliate links. Please read my disclaimer for more info.

We all know that password security is more important than ever. It’s best practice to use different passwords for every website we have an account on. But how do we remember all those passwords?

A lot of people have moved to use a dedicated password manager to control, generate, and store complex passwords for all their accounts. You may have heard of companies like LastPass or 1Password which allow you to synchronize these passwords across all your devices. I’ve been a LastPass user for many years but in an effort to move to more and more self hosted software I started looking for an alternative. That’s when I discovered Bitwarden, an open source password manager that can be used for individuals, teams and organizations. You can self host Bitwarden on your own hardware or VPS, so you can have complete control of your password management setup.

Bitwarden Features

The most exciting feature to me about Bitwarden is the ability to completely host it yourself on your own hardware. Some other features that are compelling:

2 Factor Authentication Support

Yubikey Support (I haven’t personally played with this yet, but something I’m looking into)

Desktop Applications and Browser Extensions

Mobile App Support (iOS and Android)

Command Line access

Secure password generator

Sharing passwords across a family/organization

Autofill for websites / apps

Clean and modern UI

Import from other password management systems

Overall the software has all the features you’d expect from a password manager application. Comparing it against something like LastPass (which I have experience with), the biggest feature you get is the ability to host it yourself. That allows you to have complete control over your own passwords and data, rather than it being in the hands of a third party. If you are worried about the security of this open source project, Bitwarden recently completed a security audit.

From my experience, the autofill features in Chrome and on Android are pretty on-par with LastPass and even are a little less frustrating to use in certain scenarios. The user interface also has a clean modern look that is less cluttered than LastPass.

Self Host Bitwarden

There are a few different strategies to self host Bitwarden.

First off, you can run it on your own local network. You’ll be able to read and write to the Bitwarden database using the phone application, Chrome extension or app while your devices are sitting on your network. Once you leave your network though things can get a bit interesting. If you don’t forward the ports on your router to your Bitwarden host, you will have a read-only version of the Bitwarden vault. You would need to VPN/WireGuard into your local network to make any changes. If you do forward the ports, then everything should work as advertised and you can add new passwords while away from home.

If you decide to run it on a VPS (I like Digital Ocean, get $50 free with my link), your Bitwarden instance will be available publicly, and you’ll be able to access it from anywhere.

So when deciding how to host it you should consider how comfortable you are opening ports on your router. If you’re not comfortable you can either still host it locally and only have read-only access to the vault when away from home. Or you can host it on a VPS and always have access. There are no right or wrong answers, just something to consider when choosing where to host.

Finally, if you’re not ready to self host it and want to just try it out. You can always get a free account at bitwarden.com. But where’s the fun in that!

Installing via Docker and Reverse Proxy

Now’s time to actually install and start hosting Bitwarden on your server. If you go to the Bitwarden website, you’ll find some instructions on how to get started hosting. However, the setup can be a bit complicated. The nice thing about open source projects and specifications is that there can be alternative implementations. That’s where the bitwarden_rs projects gets involved.

The bitwarden_rs project is a reimplementation of the Bitwarden server API that is compatible with all the Bitwarden clients. Meaning you can still use the Browser Extensions and Apps with bitwarden_rs. The main benefit of bitwarden_rs is that its lighter weight and easier to run than the official Bitwarden server. This is more ideal for a self-hosted deployment where you will not have a ton of users or traffic.

First off, you’re going to need a reverse proxy with HTTPS eventually. You don’t want to be serving your password management system over HTTP. For that I recommend nginx and I have a full guide on my blog on how to get started. That should take you through getting HTTPS certificates and an nginx reverse proxy working together.

Docker Compose

We’ll be using docker compose to manage the bitwarden_rs service. I like to run my docker containers as nonroot users, so mine is a little bit different than the normal docker-compose.yml file you might see for bitwarden_rs.

--- version: '3' services: bitwarden: image: bitwardenrs/server:latest user: 1000:1000 volumes: - ./config/bitwarden:/data environment: - WEBSOCKET_ENABLED=true - ROCKET_PORT=1024 ports: - 8080:1024 - 3012:3012 restart: unless-stopped 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 --- version : '3' services : bitwarden : image : bitwardenrs/server :latest user : 1000 :1000 volumes : - . /config/bitwarden :/data environment : - WEBSOCKET _ ENABLED=true - ROCKET _ PORT=1024 ports : - 8080 :1024 - 3012 :3012 restart : unless-stopped

Some key takeaways:

I make a config directory relative to the docker-compose.yml file called ./config/bitwarden . This is mounted into the container and Bitwarden will write all it’s data there. This is the directory you’ll want to make regular backups of.

. This is mounted into the container and Bitwarden will write all it’s data there. This is the directory you’ll want to make regular backups of. I’m using UID=1000 and GID=1000 for the container. This basically makes it so that the container is running as a non-root user. Also any data files created by Bitwarden will now match the user and group of the user running the container. If you are unsure of your uid/gid run the id command.

command. I’m using environment variables to enable websockets and set the port for the web server. Notice we have to set the port to at least 1024 because it’s a non root user.

Port 1024 is mapped to port 8080 on the host side. 8080 is pretty arbitrary, but we’ll use it in the next section with the reverse proxy. I didn’t want to use port 80 because it would clash with my proxy. Port 3012 is the websocket server and I just kept it the same.

The restart policy is unless-stopped, so the container should startup when the server boots up.

Once you’ve made your file startup the container by running docker-compose up bitwarden . You should be able to navigate to http://<bitwarden_server_ip>:8080 and see the Bitwarden start screen. We still need to setup HTTPS though.

Nginx

Now we need to secure our Bitwarden instance by adding HTTPS to the mix. If you haven’t already, read my guide on creating a Reverse Proxy with HTTPS without Opening Ports.

Adding Bitwarden behind the reverse proxy is quite easy. I created a new subdomain for Bitwarden so I could access it at bitwarden.example.com on my local network. We simply listen for traffic on that subdomain and route it to the ports we exposed in our container.

server { listen 443 ssl; listen [::]:443 ssl; server_name bitwarden.*; include /config/nginx/ssl.conf; client_max_body_size 128M; location / { include /config/nginx/proxy.conf; resolver 127.0.0.11 valid=30s; proxy_pass http://192.168.1.2: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; } location /notifications/hub { resolver 127.0.0.11 valid=30s; proxy_pass http://192.168.1.2:3012; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } location /notifications/hub/negotiate { resolver 127.0.0.11 valid=30s; proxy_pass http://192.168.1.2:8080; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 server { listen 443 ssl ; listen [ : : ] : 443 ssl ; server_name bitwarden . * ; include / config / nginx / ssl . conf ; client_max_body_size 128M ; location / { include / config / nginx / proxy . conf ; resolver 127.0.0.11 valid = 30s ; proxy_pass http : //192.168.1.2: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 ; } location / notifications / hub { resolver 127.0.0.11 valid = 30s ; proxy_pass http : //192.168.1.2:3012; proxy_set_header Upgrade $ http_upgrade ; proxy_set_header Connection "upgrade" ; } location / notifications / hub / negotiate { resolver 127.0.0.11 valid = 30s ; proxy_pass http : //192.168.1.2:8080; } }

Note in the configuration above I have hardcoded http://192.168.1.2 which is my server that is running bitwarden_rs. You’ll need to update that to match the host that is running your Bitwarden server. If you chose a different port than 8080 you’ll need to update that as well.

After creating your Nginx configuration file restart your reverse proxy container and try to visit Bitwarden at https://bitwarden.example.com . Create your first account and start saving your passwords!

Closing Thoughts

Password security is more important than ever and Bitwarden is a great self hosted solution. When you self host Bitwarden you have complete control over your data and privacy. If you’ve been using Bitwarden for awhile, let me know your experiences in the comments!

I’d also love to hear any other self hosted applications you’re running, always looking to move more to my own infrastructure.