Deploying / Vaultwarden
Vaultwarden
Installing the Vaultwarden template -- a password manager every Bitwarden client can use -- and inviting the first user.
Vaultwarden is a lightweight server for the Bitwarden protocol, so the official Bitwarden apps, browser extensions and CLI all work against it. The template runs it on SQLite, with everything in one volume.
| Image | vaultwarden/server:1.37.2-alpine |
| Port | 80 inside the container, behind kamal-proxy |
| Health check | /alive |
| Volumes | data |
| Singleton | yes |
Before you start
- A server that is registered, and a ready runner
- A hostname for it. Bitwarden clients will not talk to a server without HTTPS, and Vaultwarden
derives WebAuthn, email links and attachment URLs from its domain, so the environment is
blocked until it has one. Leave the domain blank and install gives it a generated
sslip.iohostname when the server has an IP – but passkeys and attachment links will be tied to that name, so enter your own domain up front if you mean to keep one.
Installing
Templates → Vaultwarden → Install. Give it a name, a runner, an environment,
the server and the domain, e.g. vault.example.com. Vaultwarden asks for nothing else.
Install creates the app and does not deploy it. Point an A record for the domain at the
server, then press Deploy.
What the install sets
| Variable | Value | |
|---|---|---|
DOMAIN |
https:// plus the environment’s first domain |
filled in at each deploy |
SIGNUPS_ALLOWED |
false |
nobody can create an account on their own |
INVITATIONS_ALLOWED |
true |
an admin invites people instead |
IP_HEADER |
X-Forwarded-For |
without it, every login shares one rate-limit bucket |
ADMIN_TOKEN |
64 random letters and digits | secret, generated once at install |
Because DOMAIN is derived at deploy time, changing the domain means deploying again.
Attachment links and passkeys registered under the old hostname will not follow it.
Inviting the first user
- Open Environment → Variables and read
ADMIN_TOKEN. Secret values are shown to admins only. - Go to
https://<your domain>/adminand paste the token. - Under Users, invite yourself by email.
- Open
https://<your domain>and create an account with the invited address.
Invitations are normally sent by email. Without SMTP configured, Vaultwarden still records the invitation, and the invited address can register directly on the sign-up page. Nobody else can.
Sending email
For invitation emails, password hints and new-device alerts, add Vaultwarden’s SMTP settings as variables on the environment and deploy:
| Variable | Example | |
|---|---|---|
SMTP_HOST |
smtp.example.com |
plain |
SMTP_FROM |
vault@example.com |
plain |
SMTP_PORT |
587 |
plain |
SMTP_SECURITY |
starttls |
plain; force_tls for port 465 |
SMTP_USERNAME |
plain | |
SMTP_PASSWORD |
secret |
Use a relay or your mail provider’s submission port. Sending straight out on port 25 is blocked on new Hetzner, DigitalOcean and AWS accounts.
Connecting clients
In any Bitwarden app, choose self-hosted on the sign-in screen and enter
https://<your domain> as the server URL.
Bitwarden clients from 2026.8.0 onwards need Vaultwarden 1.37.2 or newer. If the image on the app is ever pinned lower, it shows up as clients that will not sync, not as an error anywhere in KamalCloud.
Hardening the admin page
The generated token works as it is, and Vaultwarden logs a warning that it is stored in plain text. To replace it with an Argon2 hash, open Environment → Shell and run:
docker exec -it $(docker ps -q --filter label=service=<app name> | head -1) /vaultwarden hash
Paste the $argon2id$... string it prints into ADMIN_TOKEN and deploy. KamalCloud writes $ into
the secrets file escaped, so the hash arrives in the container unchanged. From then on, you sign
in to /admin with the password you typed into hash, not with the string.
Settings saved from the /admin page go to config.json in the data volume and take
precedence over variables. If a variable seems to be ignored, check whether it was saved
there.
Data and backups
Everything – the SQLite database, attachments, sends, icons and the RSA keys that sign
sessions – lives in the data volume, <app name>-data on the host. It is registered for
backup at install and marked to stop containers, so Vaultwarden stands
down for each backup and each restore. Both are consistent, and both are a short outage.
Set a schedule and a bucket on the volume. A password manager with no off-machine backup is one disk failure from losing every vault on it.
Upgrading
Change the image tag on the app and deploy. Read Vaultwarden’s release notes first: it upgrades the database on start, and it does not go back down.
Troubleshooting
- Blocked: “Vaultwarden reads its own address” – the environment has no domain. Add one and deploy.
- Clients sign in but will not sync – check the image is at least 1.37.2.
- Every user is rate-limited at once –
IP_HEADERhas been removed or changed. - The admin token is rejected – after switching to a hash, sign in with the password, not with the hash.