Bitwarden Local Only


This is accomplished by only decrypting the vault locally in program memory on demand, only transporting the vault to servers in encrypted form, and ensuring that authentication with the Bitwarden servers is via private secure (HTTPS) communication channels. Bitwarden keeps user data secure with 256-bit AES encryption — the same encryption used by banks and governments around the world — so you can feel secure storing your information on Bitwarden’s cloud servers. However, if you’re worried about your data being compromised in the cloud, Bitwarden also offers the option for local data storage.

This Wiki contains the info to setup a frontend Caddy reverse proxy service with a Let’s Encrypt authorized TLS certificate and a backend host running a Caddy reverse proxy / webserver which serves Nextcloud with Collabora integrated and Bitwarden_rs. The frontend Caddy will also issue TLS certificates for the backend LAN connections and renew them periodically. With this setup the connection will be encrypted from A to Z. For quick references, just scroll down to the second post which contains final and complete configuration files.

In the Wiki Use Caddy for local HTTPS (TLS) between front-end reverse proxy and LAN hosts I described how to add TLS between the downstream reverse proxy and the upstream hosts (services). This Wiki is a continuation, which results in a setup with Nextcloud and Collabora integrated and Bitwarden_rs running behind a Caddy reverse proxy with a Let’s Encrypt certificate. The connections upstream, are also encrypted with the help of the ACME server built into Caddy. This is specifically interesting for Bitwarden_rs because without, your passwords will cross your LAN unencrypted. There is no need for any other software as Caddy has everything build in to connect the pieces (services) together.

This Wiki assumes you have managed to setup a Caddy reverse proxy as explained in the Wiki Using Caddy as a reverse proxy in a home network by @matt. First reading the Wiki Use Caddy for local HTTPS (TLS) between front-end reverse proxy and LAN hosts is helpful but probably not necessary.

The definitions and requirements from the Wiki’s mentioned above apply to this Wiki too. Please note that the setup described in this setup requires a local DNS or split DNS (as described in the above Wiki).

This Wiki also assumes you have docker and docker-compose working and you know how to use this. I consider it out of scope for this Wiki to go over every docker(-compose) detail but I tried to include enough info for the less experienced readers.

The below figure shows a frontend (downstream) host and a backend (upstream) host. Each host can be virtual or physical. The frontend host is setup with Caddy as reverse proxy to the outside world and provides trusted certificates from Let’s Encrypt. The backend host act as a Caddy reverse proxy and webserver. Nextcloud, Collabora and Bitwarden_rs are installed as Docker containers on this host. Bitwarden_rs is a fork from Bitwarden which is less heavy but compatible with all the official clients.

On the top of each host you see the IP and port number and FQDN that this host is listening to.
In the backend host, you see docker containers with inside the service and the port its running on. Under each docker container you see the port that is being exposed to the backend host.

Note that Caddy has not been installed as a Docker container for a reason. It complicates the local certification renewal.

To set up the DNS you can follow the same guidance as in Using Caddy as a reverse proxy in a home network. The result should be that you have a domain name setup for each service pointing to the public IP address leased to your network ie;

Each host or service should be given a FQDN to resolve the IP of the corresponding host in the local network. This is required to make the local certificate mechanism to work. For this wiki I created:

caddy.roadrunner192.168.0.2 to reach the frontend Caddy service
nextcloud.roadrunner192.168.0.4 to reach Nextcloud service
bitwarden.roadrunner192.168.0 .4 to reach Bitwarden service

Collabora will be lifting on nextcloud.roadrunner and therefore it does not require its own FQDN like collabora.roadrunner.

It is also a good idea to setup split DNS for the public domains. That way you can access the services from you LAN without going outside the LAN. Ie

nextcloud.mydomain.com192.168.0.2 to redirect to the Caddy frontend
bitwarden.mydomain.com192.168.0.2 to redirect to the Caddy frontend

To set up the port forwarding you can follow the same guidance as in Using Caddy as a reverse proxy in a home network.

This is where the connections from the internet come into your LAN. Caddy will issue a certificate to make the connection secure with Let’s Encrypt as Certificate Authority. Depending which service the user is trying to connect to ie Nextcloud, Caddy will forward the incoming connection to the backend where the actual service is running. To make this work, the Caddyfile needs to be configured.

8.1. Caddyfile

First, to enable the ACME server in the frontend, include the acme_server directive in the Global section of the Caddyfile. Optional (recommended) you can also add debug which will help with debugging if things don’t work.

Next is to configure to forward incoming connection for Nextcloud upstream:

The line header_up Host {http.reverse_proxy.upstream.hostport} is a host header with placeholder that will override the host header with the host name in the proxy upstream.

The line header_up X-Forwarded-Host {host} is to return the host name from the reverse proxy.

If you want more background info about this, read my previous Wiki or search my posts where @francislavoie helped me with this.

To forward incoming connections for Bitwarden upstream, include:

Bitwarden_rs comes with an optional admin web console. For security reasons I do not like to expose this to the world.

respond /admin* provides users that try to access the Bitwarden admin web console the same printed message as when the admin console is disabled. Hopefully this will make them give up. You can also present a 404 error. In the LAN the admin console can still be made available.

Additional settings for Collabora are not necessary because Collabora will be integrated with Nextcloud.

Start Caddy by running Caddy start and check the output for possible errors.

This is the host where the services are running in docker containers. Caddy is installed directly (no docker to avoid certificate renewal issues) to forward the connections from the frontend to the docker containers and serve as a fileserver for Nextcloud.

9.1. Docker configuration

Although this Wiki is about Caddy, I’m including the docker-compose files that I composed too. I spend a lot of time to get a working setup and hope to provide users some guidance to a quick working setup. I’m quite sure the docker-compose files can be optimised and I am open for input for any comment or improvements.

There is one docker-compose file for Nextcloud and Collabora combined that also makes use of an .env file. The second docker-compose file is for Bitwarden_rs only.

9.2. Nextcloud and Collabora docker-compose

Below the docker-compose file to setup the docker containers for Nextcloud and Collabora.
Further down you also find an example of the required .env file. Adjust both files and save them in the same folder, ie ~/nextcloud/docker-compose.yml and ~/nextcloud/.env

Bitwarden Local Only

9.2.1. Nextcloud references and notes:

Nextcloud community – 3 Apr 20

Either use the Nextcloud fpm image or when the smbclient is desired, build the fpm image with smb included. In that case, add a folder nc_smb_image with a dockerfile that contains:

Since I use samba network drives, I did not configure the Nextcloud data dir. If you do want to use local storage, uncomment NEXTCLOUD_DATA_DIR=/srv/nextcloud/data and don’t forget to change the path.

9.2.2. Collabora references and notes:

Collabora Office and Collabora Online

Add your desired dictionaries
SSL has been enables without termination. Although this should not be necessary, it was the only way to get it working. Disabling SSL will break the setup.

9.2.3. .env file

The above docker-compose file us making use of the below .env file. Tweak it and save it in the same folder as the docker-compose.yml

In this Wiki NEXTCLOUD_FQDN and COLLABORA_FQDN are both set the same because I integrated Collabora with Nextcloud.

9.2.4. Run Nextcloud and Collabora docker containers

Now that both the docker-compose.yml and .env files are completed, run docker-compose up in the folder where you saved the files or docker-compose up -d to run in the background.

Once the downloading and compiling is done, run docker ps to confirm you have 3 docker containers running ie:

9.2.5. Configure Nexcloud config.php

Once you confirmed the docker containers are running, locate the file /var/www/html/config/config.php and update the trusted domains, trusted proxies and the overwrite.cli.url, ie:

Restart Nextcloud with docker restart nextcloud to apply the changes.

9.3. Bitwarden_rs

I highlight again that in this Wiki I have used Bitwarden_rs which is a lighter version of the official Bitwarden project. Copy and adjust the below docker-compose file and save it in e.g. ~/bitwarden_rs/docker-compose.yml

9.3.1. Bitwarden_rs references and notes:

In this example config for security reasons the signups are disabled. New users can only signup after an invitation.
The admin console is enabled and can be reached within the LAN only via bitwarden.roadrunner/admin. Access through has been blocked in the frontend Caddyfile.

Please visit the github repository for additional info about the setup.

Bitwarden Local Network Only

Run docker-compose up or docker-compose up -d to start the Bitwarden_rs container. Now you should have 4 docker containers running.

9.4. Caddyfile

Now that all the docker containers are up and running, continue to setup Caddy on the backend to request local certificates from the frontend Caddy. The easiest way is to do this in the global section so that it applies for the whole Caddyfile:

To request a certificate, a root certificate from the server is required. Once you have started the Caddy service on the frontend for the first time and there are no errors, a file named root.crt is generated in .local/share/caddy/pki/authorities/local

You will have to copy this file to the backend and point to it in the Caddyfile. In the above example, the file root.crt has been copied to /etc/ssl/certs/root.crt

Once this is done, you can start adding the local domains that host the services:

9.4.1. Nextcloud and Collabora notes and references:

Collabora CODE (port from examples/collabora at master · caddyserver/examples · GitHub)This is an example configuration of how to use Collabora CODE with caddy. Collabora can then be used with, for example, NextCloud. Note: In this example file Collabora CODE is started using its official docker container and reachable in the internal network using https://collabora:9980. { encode gzip @collabora { path /loleaflet/* # Loleaflet is the client part of LibreOffice…
The Nextcloud Quick reference on Docker Hub states that there are two versions (apache or fpm) of the Nextcloud image. The apache version contains a full Nextcloud installation including an apache web server. The fpm version must be combined with any webserver that can proxy the http requests to the FastCGI-port of the container. In the Quick reference fpm example, an nginx container is combined with the Nextcloud-fpm image and a MariaDB database container. If you would rather use Caddy in plac…

I also used @Basil post here which was very helpful getting Collabora to work.

The Collabora docker container has its own web server and therefore we can only forward the connection instead of using Caddy to act as a web server. While discussing the docker-compose file I mentioned that the SSL options for Collabora had to be enabled. Because an untrusted certificate is used, we need add tls_insecure_skip_verify to have Caddy accept the connection.

Similar to the frontend Caddyfile, the line header_up Host {http.reverse_proxy.upstream.hostport} will override the host header with the host name in the proxy upstream.

9.4.2. Bitwarden notes and references:

The above setup is mostly copied from the Github wiki with the addition of the host header header_up Host.

10. Running and testing

At this point al(most) everything has been configured. It is time to test things.

  1. If not already, start Caddy on the frontend and backend with caddy run or caddy start (background).
  2. If not already, start all the docker containers by running docker-compose up -d in ~/nextcloud and ~/bitwarden_rs.

Verify that all 4 docker containers are running with the command docker ps if the answer is yes, lets try to connect to the services directly on the backend. Knives out popcorn time.

Lets test Nextcloud:
Browse to https://nextcloud.roadrunner and if all goes well you should see the following warning:

That is correct because you didn’t add the hostname nextcloud.roadrunner to the trusted list.

Now lets test Collabora:
First run curl -k https://localhost:9980 on the host where the Collabora docker container is running. It should return OK

Next browse to https://nextcloud.roadrunner/hosting/capabilities If you use Firefox, you should see something like the following:

Now browse to https://nextcloud.roadrunner/hosting/discovery and you will see a big list that starts like this:

Very important to note is the urlsrc. This should be pointing to your public domain. If not, something is wrong with the Caddy setup.

Bitwarden Local Only Access

Another test is to browse to https://nextcloud.roadrunner/loleaflet/dist/admin/admin.html and enter user:admin and the password that you provided in the .env file. If all goes well you will see the admin console:

Last to test is Bitwarden_rs.
Browse to https://bitwarden.roadrunner to find the login console.

If you managed to access all the services directly on the backend host, it is time to test if you can also access them throught the frontend. If you setup split DNS for your public domains, you can now browse to and you should get the setup page for Nextcloud. Here you can complete the Nextcloud installation.

Bitwarden Local Only Restaurants

When you go you get the login page for Bitwarden.

If you didn’t setup split DNS, the above may not work, depending on your network setup. In that case you have to try connecting from outside the LAN ie with a cellphone or with another internet connection. If you cannot connect from outside the LAN, probably something is wrong with your DNS.

Only one thing left to configure. When the Nextcloud installation has finalized, login as admin and go to Settings, Administration, Collabora Online Development E..

In the following screen you have to enter the URL of the Collabora Online-server. This is the same URL as your Nextcloud public domain. Ie

After clicking save you should have the green check. Now when you open or create an office document in Nextcloud, Collabora will be started and you can start editing your document.

If Collabora is not starting, first make sure that the domain in the URL is not being changed in your session. If it does, something is wrong in your Caddyfiles.

That is it! Now you have a setup that provides an ecrypted connection to your own hosted Nextcloud + Collabora or Bitwarden_rs services from any device with internet.

12. Final word and disclaimer

Well, if you made it until here, I guess a few words more won’t hurt
It took me quite some time to gather all the info required to create this setup. I hope it will set you off quickly. I tried to link as much as possible to my info sources. I did however left too much time in between to write and finalize this Wiki and I may have left some (crucial) things out unintentionally. Sorry if that happened but please let me know and I will try to update this Wiki. I’m also sure the setup can be improved on some points and it would be great to receive inputs for this.

Password managers are very useful utilities that store (and generate) unique and lengthy passwords. Many utilities exist to store passwords locally (pass, EncryptionWizard, etc), but I need my passwords synced across several devices. Dozens of password managers exist that perform multi-device sync. But, many services require storing passwords on their servers. Some allow storing encrypted stores on your cloud (Dropbox, OneDrive, etc). However, I want my password stores to exist 100% under my local control. ARM development boards, like the Beagle Bone Black Wireless, provide a nice low-cost, low-power platform to run a password manager store. Additionally, the device can be easily powered down to take the password store offline. A Raspberry Pi should also work, but I was lacking one on hand.

Bitwarden is the only open source password manager I've discovered that allows self-hosting the server and also provides open source iOS, Android, Linux, OS X, and Windows clients. Unfortunately, the official Bitwarden server does not support ARM because of a mssql dependency. Joshua Stein wrote a nice Ruby server supporting the Bitwarden API that can be self-hosted on ARM devices. (Servers written in golang and Rust also exist.)

Running rubywarden on the Beagle Bone Black Wireless only allows syncing passwords between devices when they are on the same network as the BBBW. Trading the 'inconvenience' of local-only sync for 100% control of my password store is well worth it, in my opinion.

Note: 8bit Solutions LLC has graciously open sourced Bitwarden. Show your support for open source companies by purchasing a premium membership even if you self-host. High quality software does not write itself.

Initial Setup

  1. Install dependencies

# apt-get install bundler libsqlite3-dev

# gem install bundler

Bitwarden Local Only Connection

  1. To slightly improve security, a utility account named rubywarden will be used to run the server.

# adduser --disabled-password --disabled-login rubywarden

  1. Clone the rubywarden repository into /opt

    $ cd /opt

    # git clone

    # chown -R rubywarden /opt/rubywarden

    # sudo su rubywarden

  2. Create the necessary directory structure for rubywarden

    $ cd rubywarden

    $ mkdir -p db/production

  3. Install the necessary ruby dependencies

    $ bundle install

  4. Before the first run, the rubywarden database must be initialized

    $ env RACK_ENV=production bundle exec rake db:migrate

  5. rubywarden does not allow new user sign-up unless the environmental variable ALLOW_SIGNUPS is true. To launch the server and allow sign-ups run the following command. Subsequent launches do not require the environmental variable.

    $ env RACK_ENV=production ALLOW_SIGNUPS=1 bundle exec rackup -p 4567

  6. Bitwarden provides a variety of client installs. Choose the appropriate one and click the gear icon on the splash screen to add the self-hosted server.

  1. Create an account and start managing passwords! Note: If testing with the iOS client, please read the dedicated iOS section below.


It's really useful to have rubywarden run when the BeagleBone is powered up. Writing a systemd unit file to provide startup functionality is fairly straightforward.

Create /etc/systemd/system/rubywarden.service and add the following:

Enable and start the service. Use journalctl -u rubywarden to debug any issues.

Compatility with iOS app

The Bitwarden AppImage seems to function just fine without rubywarden using HTTPS. By default, it is only using HTTP. However, the iOS client requires HTTPS.

In order to support HTTPS, the Apache webserver (already running on the BBBW) will be configured to serve HTTPS and function as a proxy to the rubywarden server. Since rubywarden is not internet accessible, Let's Encrypt certificates don't make sense; instead a self-signed certificate will be used for HTTPS. In order for the self-signed certificate to be usable on iOS, a Certificate Authority certificate will need created and installed on the iOS device.

Note: Apple changed trusted certificate requirements in iOS 13 requiring an extendedKeyUsage flag to be set in the certificate.

  1. Create the CA certificate

    $ openssl genrsa -out rubywardenCA.key 2048

    $ openssl req -x509 -sha256 -new -key rubywardenCA.key -out rubywardenCA.crt -subj /CN='rubywarden CA'

  2. Send the rubywardenCA.crt certificate to the iOS device via e-mail and follow the prompts to install. After installation, use the Settings app to navigate to General->About->Certificate Trust Settings and toggle rubywarden CA on. This means that iOS will treat any certificate signed by the CA as a valid HTTPS connection.

  3. Generate a certificate for Apache to use

    $ openssl genrsa -out rubywarden.key 2048

    $ openssl req -new -out rubywarden.req -key rubywarden.key -subj /CN=beaglebone.local

    $ openssl x509 -req -sha256 -in rubywarden.req -out rubywarden.crt -CAkey myCA.key -CA myCA.cer -days 365 -CAcreateserial -CAserial serial -extfile <(printf 'extendedKeyUsage = serverAuthnsubjectAltName=DNS:beaglebone.local')

  4. The keys created above can be used to perform mitm attacks if they are compromised. To improve security (slightly, the SD card can just be removed from the BBBW) move them to /root/certs/beaglebone.local/ and make the keys read-only.

    # mkdir -p /root/certs/beaglebone.local/

    # mv rubywarden.* /root/certs/beaglebone.local

    # chmod 400 /root/certs/beaglebone.local/*.key

  5. Finally, set up Apache to serve as an HTTPS proxy. Append the following VirtualHost entry to /etc/apache2/sites-enabled/000-default.conf Relaunch Apache with # systemctl restart apache2.service after making the edits.

Did your security posture improve because of this post? Consider saying thanks by using my Amazon Affilliate URL and help to keep this site ad & analytics free.