Run Vaultwarden on Fly.io with reliable Litestream SQlite replication and attachments/sends stored in S3.
Using the smallest VM size on Fly.io (shared-cpu-1x
) and leveraging the Tigris object storage free tier, this
costs approx. 2 USD/mo to run (depending on the region). Small Vaultwarden instances won't see traffic 24/7, so
you should pay much less because your VM can be stopped for a large portion of the time.
-
Create a new Fly.io application
$ fly app create <app_name>
-
Create an S3 object storage bucket for your app.
$ fly storage create --app <app_name> --name <app_name>
-
Create secrets:
$ fly secrets set \ VAULTWARDEN_RSA_PRIVATE_KEY="$(openssl genrsa 2048)" \ AGE_SECRET_KEY="$(age-keygen | tail -n1)"
-
Create an admin password, if you want to use the Vaultwarden admin panel. Note that you cannot make any changes to the Vaultwarden configuration via the admin panel, because the
config.json
is built entirely from environment variables on startup.$ docker run -it --rm ghcr.io/dani-garcia/vaultwarden /vaultwarden hash
Because the admin password is already hashed, you can set it in your
fly.toml
's[env]
section instead of usingfly secrets set
. -
Create a copy of
fly.example.toml
and update theapp
name. -
Run
fly deploy
-
Run
fly scale count 1
(this application does not support high-availability, and by default, the initial deployment step sets the machine count to2
).
First you should install Vaultwarden on Fly.io. Then you should ensure that while you are migrating, no modifications can be made to your existing Vaultwarden installation. If you can't easily turn off the Vaultwarden installation without loosing access to the filesystem (e.g. if it is deployed in Kubernetes), make sure to perform a WAL checkpoint on the SQlite database before downloading it.
sqlite> PRAGMA wal_checkpoint(TRUNCATE);
Then copy the existing SQlite database to the S3 bucket with the key import-db.sqlite
and redeploy your app with
IMPORT_DATABASE
set to true
. This will make the startup sequence fetch the database from the S3 bucket instead
of restoring the existing backup with Litestream.
$ mc cp db.sqlite3 tigris/my-vaultwarden-bucket/import-db.sqlite
$ fly deploy --env IMPORT_DATABASE=true
Once that is complete, check the app logs to ensure that the database was imported from the S3 bucket and that the
Litestream replication has completed. Redeploy your application without the IMPORT_DATABASE
variable.
$ fly deploy
Copy your existing Vaultwarden installation's RSA private key to a Fly secret:
$ fly secrets set VAULTWARDEN_RSA_PRIVATE_KEY="$(cat rsa_key.pem)"
And copy your existing installations' attachments, sends and optionally icon cache to the S3 bucket:
$ mc cp --recursive attachments sends icon_cache tigris/my-vaultwarden-bucket/data/
Last but not least, check that all relevant configuration options in your existing installations' config.json
or environment variables are also set as the corresponding VAULTWARDEN_*
environment variables or secrets in your
Fly.io app. And that should be it!
The Backing up your Vault documentation for
Vaultwarden explains the purpose of each the files and directories in the /data
directory. Since we're running
on an ephemeral disk, we need to have an alternative story around the persistence of these files. This section
describes how the data that would usually live on a persistent disk survives:
Path | Persistence implementation |
---|---|
/data/attachments |
Re-configured to /mnt/s3/attachments . |
/data/icon_cache |
Re-configured to /mnt/s3/icon_cache . |
/data/sends |
Re-configured to /mnt/s3/sends . |
/data/config.json |
Auto-generated on startup from environment variables. |
/data/db.sqlite |
Replicated to S3 via Litestream to vaulwarden.db/ in the bucket. |
/data/rsa_key.pem |
Initialized from VAULTWARDEN_RSA_PRIVATE_KEY environment variable. |
/data/rsa_key.pub.pem |
Initialized from VAULTWARDEN_RSA_PRIVATE_KEY environment variable. |
The /mnt/s3
directory is a GeeseFS mount to the data/
path in the same S3 bucket that the SQlite database
is backed up to.
Note that because we generate the
config.json
from environment variables, modifying it in the Admin UI or organization settings will not work. These settings must be changed from environment variables in yourfly.toml
or viafly secrets set
.
S3 configuration
Variable | Default | Description |
---|---|---|
AWS_ACCESS_KEY_ID |
n/a, required | |
AWS_SECRET_ACCESS_KEY |
n/a, required | |
AWS_REGION |
n/a, required | |
AWS_ENDPOINT_URL_S3 |
n/a, required | |
BUCKET_NAME |
n/a, required |
Secrets
Vaultwarden configuration
Variable | Default | Description |
---|---|---|
VAULTWARDEN_ADMIN_TOKEN |
n/a, required | Token to enter the Vaultwarden admin panel with. Create it with docker run -it --rm ghcr.io/dani-garcia/vaultwarden /vaultwarden hash , may be stored as a non-secret. |
VAULTWARDEN_RSA_PRIVATE_KEY |
n/a, required | The RSA 2048-bits private key that Vaultwarden uses to sign JWTs. Generate with openssl genrsa 2048 . If you change this value, all current JWTs are invalidated. |
VAULTWARDEN_LOG_LEVEL |
info |
|
VAULTWARDEN_DOMAIN |
https://${FLY_APP_NAME}.fly.dev |
The public URL of your Vaultwarden deployment. |
VAULTWARDEN_SENDS_ALLOWED |
true |
|
VAULTWARDEN_HIBP_API_KEY |
(empty string) | Have I been Pwnd! API Key |
VAULTWARDEN_SIGNUPS_ALLOWED |
true |
|
VAULTWARDEN_SIGNUPS_VERIFY |
false |
|
VAULTWARDEN_SIGNUPS_VERIFY_RESEND_TIME |
3600 |
|
VAULTWARDEN_SIGNUPS_VERIFY_RESEND_LIMIT |
6 |
|
VAULTWARDEN_INVITATIONS_ALLOWED |
true |
|
VAULTWARDEN_EMERGENCY_ACCESS_ALLOWED |
true |
|
VAULTWARDEN_EMAIL_CHANGE_ALLOWED |
true |
|
VAULTWARDEN_PASSWORD_ITERATIONS |
600000 |
|
VAULTWARDEN_PASSWORD_HINTS_ALLOWED |
true |
|
VAULTWARDEN_SHOW_PASSWORD_HINT |
false |
|
VAULTWARDEN_INVITATION_ORG_NAME |
Vaultwarden |
|
VAULTWARDEN_DISABLE_2FA_REMEMBER |
false |
|
VAULTWARDEN_USE_SENDMAIL |
false |
|
VAULTWARDEN_ENABLE_DUO |
false |
|
VAULTWARDEN_ENABLE_SMTP |
false |
|
VAULTWARDEN_ENABLE_EMAIL_2FA |
value of VAULTWARDEN_ENABLE_SMTP |
|
VAULTWARDEN_ENABLE_SMTP |
false |
|
VAULTWARDEN_SMTP_HOST |
n/a, required if SMTP enabled | |
VAULTWARDEN_SMTP_SECURITY |
force_tls | |
VAULTWARDEN_SMTP_PORT |
465 | |
VAULTWARDEN_SMTP_FROM |
n/a, required if SMTP enabled | |
VAULTWARDEN_SMTP_FROM_NAME |
Vaultwarden | |
VAULTWARDEN_SMTP_USERNAME |
n/a, required if SMTP enabled | |
VAULTWARDEN_SMTP_PASSWORD |
n/a, required if SMTP enabled | |
VAULTWARDEN_ENABLE_YUBICO |
false |
|
VAULTWARDEN_YUBICO_CLIENT_ID |
n/a, required if Yubico enabled | |
VAULTWARDEN_YUBICO_SECRET_KEY |
n/a, required if Yubico enabled |
GeeseFS variables
Variable | Default | Description |
---|---|---|
GEESEFS_ENABLED |
true |
If set to false , GeeseFS will not be used and related data directories will not be mounted. Use with care, this is for testing only. |
GEESEFS_MEMORY_LIMIT |
64 |
The memory limit in MB for GeeseFS. |
Litestream variables
Variable | Default | Description |
---|---|---|
AGE_SECRET_KEY |
n/a, required | |
LITESTREAM_ENABLED |
true |
Whether to restore and replicate the SQlite database with Litestream. You likely never want to turn this option off, as you will loose your SQlite database on restarts. |
LITESTREAM_RETENTION |
24h |
Configure the Litestream retention period. Retention is enforced periodically and can be changed with LITESTREAM_RETENTION_CHECK_INTERVAL . |
LITESTREAM_RETENTION_CHECK_INTERVAL |
1h |
The interval at which retention should be applied. |
LITESTREAM_VALIDATION_INTERVAL |
12h |
The interval at which Litestream does a separate restore of the database and validates the result vs. the current database. |
LITESTREAM_SYNC_INTERVAL |
10s |
Frequency in which frames are pushed to the replica. Note that Litestream's typical default is 1s , and increasing this frequency can increase storage costs due to higher API request counts. |
Maintenance variables
Variable | Default | Description |
---|---|---|
ENTRYPOINT_IDLE |
false |
If set to true , enter idle mode before launching the application or if an error occurs on startup. Note that Fly.io might stop the machine after a short while. |
IMPORT_DATABASE |
false |
If set to true , the startup process will check for an import-db.sqlite file in the S3 bucket and load that instead of litestream restore . Use for migrating from another Vaultwarden instead. Should be turned off immediately after the litestream replication succeeded. |