I have been using LastPass for password management since like forever.

I became a paid customer in 2014. It was initially $1/month. Then it got hiked to $2/month and currently it stands at $3/month. That’s $36/year which was working out too much for me.

Furthermore, the free option is limited to a single type of device. Since I wanted to use it across desktops and mobiles, there was no option except for the $3/month plan.

I had been looking at bitwarden for quite some time now but read that it’s too resource heavy. I then came to know of biwarden_rs which is a rewrite of bitwarden in Rust and compatible with upstream Bitwarden clients. I had then forgotten about this for some time until I head the news of it being renamed to vaultwarden via a Reddit thread.

I decided to install it via Docker as it seemed the easiest thing to do. I also wanted to run the alpine image since that was the most light-weight image.

docker

Pull the alpine docker image

docker pull vaultwarden/server:alpine

By default, the container runs using the SQLite database, which suits me just fine!

Ensure that volumes and ports are mapped correctly and issue the run docker command.

docker run -d --name vaultwarden -v /vw-data/:/data/ -p 8080:80 vaultwarden/server:alpine

I mapped the port 8080 of the vaultwarden server to port 80 of the container since I wanted it to route it via Nginx to take advantage of the SSL that I had already set up there.

nginx

Nginx configuration

server {
  listen 443 ssl;
  listen [::]:443 ssl;

  server_name vault.<your-domain>;

  access_log /var/log/nginx/vaultwarden_access.log;
  error_log /var/log/nginx/vaultwarden_error.log;

  gzip on;
  gzip_types      text/plain application/xml;
  gzip_proxied    no-cache no-store private expired auth;
  gzip_min_length 1000;

  ssl_certificate <path to ssl cert>;
  ssl_certificate_key <path to ssl key>;

  location / {
    proxy_pass http://127.0.0.1:8144;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $http_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-Host $server_name;
    proxy_set_header X-Forwarded-Proto https;
    proxy_connect_timeout 2400;
    proxy_read_timeout 2400;
    proxy_send_timeout 2400;
  }

  location /notifications/hub/negotiate {
    proxy_pass http://127.0.0.1:8144;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $http_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-Host $server_name;
    proxy_set_header X-Forwarded-Proto https;
    proxy_connect_timeout 2400;
    proxy_read_timeout 2400;
    proxy_send_timeout 2400;
  }

  location /notifications/hub {
    proxy_pass http://127.0.0.1:3012;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    }

    # These "harden" your security
    add_header 'Access-Control-Allow-Origin' "*";
}


server {
  listen 80;
  listen [::]:80;

  server_name vault.<your-domain>;

  if ($host = vault.<your-domain>) {
    return 301 https://$host$request_uri;
  }

  return 404;
}

After checking everything using nginx -t, I reloaded nginx using nginx -s reload command. And going to the URL I was presented with this page.

bitwarden login

So, I created an account and was set.

other considerations

But then I thought – if I could create an account, then anybody could! So, going back to the docs, I noticed that there were two other options that I need to run the container with –

SIGNUPS_ALLOWED=false and INVITATIONS_ALLOWED=false. With these two options, nobody else can create an account. Also, people invited to your application, cannot in turn invite others.

admin page

One drawback of disallowing invitations is that even the owner cannot invite another user. For this, the admin page needs to be enabled.

From the docs, this needs an extra configuration option to the docker run command.

docker command

Since I had rerun the docker command again with additional configuration options, I need to stop and remove the running container and start a fresh container (from the earlier pulled image) with the new configuration.

To stop the container, get it’s id using docker ps

CONTAINER ID   IMAGE          COMMAND                  CREATED
9de22df311c4   vaultwarden    "/usr/bin/dumb-init …"   2 seconds ago

and stop it using docker stop <contaienr id> which in my case was 9de2

Once stopped, the container also needs to be removed completely using docker rm -rf <container id>

Now, the final docker run command becomes –

docker run -d --name vaultwarden \
    -e ADMIN_TOKEN=some_long_random_string \
    -e SIGNUPS_ALLOWED=false \
    -e INVITATIONS_ALLOWED=false \
    -v /vw-data/:/data/ \
    -p 8080:80 vaultwarden/server:alpine

Make sure that the some_long_random_string is not something that could be guessed easliy!

browser extensions

The next step was to download the corresponding browser extensions from the bitwarden download page and login individually.

Once the browser extension has been added, we need to point it to our server. So, click on the extension, click on the Settings icon on top left and enter your server URL under the heading Self-Hosted Environment.

environment setup

two factor authentication

Additionally, we can add a multi-factor authentication via authentication apps from the Settings > Two-step Login.

two factor auth

Finally, the only step remaining was to export all the passwords out of LastPass and import them into Vaultwarden. This is available under Tools > Import Data

bitwarden import

All done.

Another advantage I get with Vaultwarden / Bitwarden is that I can add an Organization and share passwords across with others if required. There is also the option to hide the passwords from those the credentials have been shared with. This option is there in LastPass too but they charge another $1/month for it.