This is a write-up I did while I figured out how to self-host Bitwarden for free using a free-tier Cloud product. The end product is a Github repo you can find here. The
readme.md there is enough to get going if you’re a technical user, whereas the text below has a bit more detail if you need it.
I’ve been meaning to self-host Bitwarden for some time. If you don’t know about Bitwarden, it’s a password manager that’s open source and allows you to host it yourself! Now my encrypted password data is even more in my control. While I have a home server, I want to limit its exposure to the public Internet. Any service you expose to the Internet can become a pivot point to the rest of your internal network.
I saw that Google Cloud offers an ‘always free’ tier of their Compute Engine. Will one shared core and 614 MB of memory be enough for Bitwarden? According to the system requirements Bitwarden requires 2GB of RAM, but reports in its Github issue tracker say that even that is not enough. I went through the trouble of trying it out anyway and it failed spectacularly, the install script couldn’t even finish. There is, however, a lightweight alternative: Bitwarden RS. It’s written in Rust and an ideal candidate for a micro instance.
Step 1: Set up a new VM
At the time or writing this, Google offers one free Google Compute Engine f1-micro instance with the following specifications:
* Region: * Oregon: us-west1 * Iowa: us-central1 * South Carolina: us-east1 * 30 GB-months HDD * 5 GB-month snapshot storage in the following regions: * Oregon: us-west1 * Iowa: us-central1 * South Carolina: us-east1 * Taiwan: asia-east1 * Belgium: europe-west1 * 1 GB network egress from North America to all region destinations (excluding China and Australia) per month
To get started, go to Google Compute Engine (after doing all the necessary setup of creating a project, and providing billing info if necessary - don’t worry, this will cost exactly $0.00 each month if done correctly) and open a Cloud Shell. You can create the instance manually, but the Cloud Shell makes everything easier. In the Cloud Shell (a small icon in the upper right corner of your Google Cloud console), the following command will build the properly spec’d machine:
$ gcloud compute instances create bitwarden \ --machine-type f1-micro \ --zone us-central1-a \ --image-project cos-cloud \ --image-family cos-stable \ --boot-disk-size=30GB \ --tags http-server,https-server \ --scopes compute-ro
You can change the zone if you’d like, however only some have the f1-micro machine-type available. The tags open up the firewall HTTP and HTTPS (HTTP is required later). I’m using the maximum free HDD because apparently I get higher IOPS and it will allow me to maximize the amount of encrypted attachments I can have on this.
I am using the stable Container Optimized OS (COS) for several reasons, primarily:
- It’s optimized for Docker containers - less overhead to consume RAM
- It’s secure by default - security updates are automatically installed and security is locked down by default
CoreOS was also a contender but it used more memory at idle in my limited testing.
Step 2: Prepare DNS
Since Google is now charging $.004 an hour for a static IP, to keep this absolutely free I am using an ephemeral IP. I provide a dynamic DNS script (
ddns.sh in the repo) that runs in docker to update a Cloudflare DNS entry. If you’re using another DNS provider this will be a bit different for you, you’re on your own but it should be pretty easy to come up with another script that will work.
Cloudflare makes it pretty easy to update DNS records with their API. My script does two things:
- Get my external IP from Google directly using their gcloud utility
- Push that IP to Cloudflare using their API.
You need to have a DNS A record ready on Cloudflare to update with the script.
Step 3: Pull and Configure Project
Pull open a SSH console on your new instance and pull the github project I threw together (make sure to do this from your home directory,
$ git clone https://github.com/dadatuputi/bitwarden_gcloud.git $ cd bitwarden_gcloud
Before you can start everything up, you need to set up the docker-compose alias by running the
utilities/install-alias.sh script (you can read more about why this is necessary here). The script just writes the alias to
~/.bash_alias and includes it in
$ sh utilities/install-alias.sh $ source ~/.bashrc $ docker-compose --version docker-compose version 1.25.5, build 8a1c60f
Feel free to explore the rest of the repository, but the only thing you should have to configure is the
.env file found in the repository root. It is a collection of environmental variables that are passed into the different containers. It should be commented enough to understand what you need to provide.
Step 4: Start Services with
To start up, simply use the command in the same directory as
$ docker-compose up
Normally, you’d include a
-d, as in
$ docker-compose up -d, however the first time is nice to see the initial startup. You should see the
caddy service auto-negotiate a Let’s Encrypt SSL cert, for example.
Now it’s time to test it out - fire up a browser and go to the URL you used.
Step 5: Start Using Bitwarden
As you filled out
.env, you should have noticed that there were two ways to get Bitwarden going. By default, you can’t create an account but instead should use the
https://<your-domain>/admin portal (accessible only with the 48-character key you generated and put in
From this page, create all the accounts you want. Once you have access, you don’t need to access the
/admin portal and should disable it by going back to your
.env and removing the 48-character key. From Bitwarden you can create an organization and invite all the people that you’d like.
You should now have a free self-hosted instance of Bitwarden that survives server reboots with an OS that gets timely security patches automatically.
There’s plenty of tweaking and optimization possible, feel free to make this yours. There were many resources that I used to build this guide, many of them listed below. Feel free to comment with any optimizations or issues that you run across.
Leave a comment
Your email address will not be published. Required fields are marked *