
Build a private vault with controlled, secure remote access
Vaultwarden is an unofficial, community-maintained implementation of the Bitwarden Client API designed for self-hosted deployments. This guide installs it with Docker Compose, keeps the service bound to loopback, publishes it through a Localtonet HTTP tunnel, and explains exactly where HTTPS terminates. It also covers account lockdown, client setup, Raspberry Pi storage, encrypted backups, restoration testing, updates, outage behavior, and practical troubleshooting.
๐ What's in this guide
What Vaultwarden is, and what it is not
Vaultwarden, formerly called bitwarden_rs, is an alternative server implementation of the Bitwarden Client API written in Rust. It is not produced by or affiliated with Bitwarden, Inc. The project provides its own server, a modified web vault bundled with its container images, and support for many familiar Bitwarden capabilities.
The project describes its API implementation as nearly complete. That distinction matters. Official Bitwarden browser extensions, mobile applications, and desktop clients are commonly used with Vaultwarden, but compatibility should not be treated as universal or permanent. A newly released client feature can depend on an API behavior that Vaultwarden has not implemented yet. Test the specific clients and workflows your users need before treating an upgrade as production-ready.
Vaultwarden recommends its published container images for installation. Community packages and source builds are alternatives, but a community package may lag behind current releases or change configuration conventions. This tutorial therefore uses the official Vaultwarden container image with Docker Compose and a persistent host directory.
Do not contact official Bitwarden support about a Vaultwarden server. Report Vaultwarden problems through the Vaultwarden GitHub project, its discussions, or the Vaultwarden community forum. Before reporting a client problem, record the Vaultwarden version, client version, relevant logs, and whether the same operation works in the bundled web vault.
Durable deployment distinctions
The meaningful choice is not a volatile price or container count. It is who operates the server, who supports it, and who is responsible for recovery. Product pricing, resource requirements, feature entitlements, and supported deployment methods can change, so they should be checked directly before making a long-term decision.
| Deployment model | Operator responsibility | Client and support relationship | Recovery responsibility |
|---|---|---|---|
| Vaultwarden self-hosted | You maintain the host, container, storage, TLS access path, updates, monitoring, and backups. | Unofficial server with a nearly complete Bitwarden Client API implementation. Support is community-based. | You must preserve the database, attachments, Sends, signing keys, configuration, and required secrets. |
| Official Bitwarden self-hosted | You operate an official deployment using the currently supported Bitwarden installation path. | Official server and official client relationship. Support availability depends on the applicable offering. | You remain responsible for infrastructure and the documented backup and recovery process. |
| Hosted password-manager service | The provider runs the service infrastructure, while you manage accounts, credentials, recovery options, and endpoint security. | Provider-controlled server and client release cycle. | Provider resilience does not replace account recovery planning, secure exports, or organization-specific continuity controls. |
Self-hosting gives you operational control, but it also makes availability and recovery your job. A database on hardware you control is not automatically private, secure, or resilient. Host compromise, weak credentials, an exposed administration panel, an untrusted backup destination, or an untested restore can still put the vault at risk.
HTTPS, secure contexts, and the actual trust boundary

Vaultwarden's web vault uses browser cryptography and requires HTTPS and a secure context. Browsers generally treat a valid HTTPS origin as potentially trustworthy. Loopback origins such as http://localhost also receive special secure-context treatment in common browsers, which is useful for same-machine testing. An ordinary LAN address such as http://192.168.1.20 is not equivalent to localhost and should not be used as the remote web-vault address.
The phrase โany HTTPS URL worksโ is too broad. The certificate must be valid for the requested hostname and trusted by the device. An expired certificate, hostname mismatch, incomplete certificate chain, or untrusted private certificate authority can produce warnings or prevent a native client from connecting. Never instruct users to ignore a certificate warning for a password manager.
http://localhost:8080 can be used on the host to confirm that Vaultwarden responds. It is not the remote client address.
HTTPS options
| Method | Certificate model | Inbound network requirement | Operational consideration |
|---|---|---|---|
| Reverse proxy with a public certificate | A reverse proxy such as Caddy or Nginx presents the certificate. | Depends on certificate validation and routing. ACME HTTP-01 normally needs inbound port 80, TLS-ALPN-01 normally needs 443, while DNS-01 can validate through DNS without those inbound validation ports. | You maintain DNS, certificate renewal, reverse-proxy configuration, and router or firewall exposure. |
| Private CA or self-signed certificate | You create and distribute the trust anchor. | No public validation is required. | Every browser, operating system, and native client must trust the issuing CA correctly. Certificate warnings are not an acceptable steady state. |
| Localtonet HTTP tunnel | The public endpoint presents HTTPS and forwards requests through the outbound tunnel. | No inbound router port forwarding or public IP address is required. | The selected Localtonet client must remain connected and the tunnel must remain started. Plan-specific timeout and availability behavior also applies. |
Where TLS ends in this tutorial
This deployment intentionally binds Vaultwarden to 127.0.0.1:8080. A remote Bitwarden client connects to the assigned Localtonet HTTPS endpoint. HTTPS is terminated at the public tunnel edge, and traffic is carried through the outbound Localtonet tunnel to the Localtonet client. The final connection from that client to http://127.0.0.1:8080 is local HTTP.
That is not the same as TLS terminating inside Vaultwarden. The trust boundary includes the public tunnel endpoint, relay path, Localtonet client process, host operating system, Docker runtime, and Vaultwarden container. Keeping the Localtonet client and Vaultwarden on the same trusted host confines the final HTTP hop to loopback. If the client forwards to a different machine on a LAN, that final hop crosses the LAN in clear HTTP unless you independently add TLS there.
The remote connection has public HTTPS, but Vaultwarden receives HTTP on its loopback listener in this design. If your threat model requires TLS all the way to the application or a local reverse proxy, configure and validate that additional TLS layer separately.
Prerequisites and supported installation scope
This tutorial covers a 64-bit Linux host, including a supported 64-bit Raspberry Pi installation, with Docker Engine and the Docker Compose plugin. Docker and Compose must be installed using the supported method for your operating system. The convenience script previously shown in this article is intentionally omitted because Docker documents caveats for that route and it should not be presented as the default production installation method.
- A maintained 64-bit Linux distribution supported by Docker Engine.
- Docker Engine and Docker Compose v2 installed from the appropriate package repository or supported platform package.
- A non-root administrative account with controlled Docker access.
- A persistent filesystem with enough capacity for the database, attachments, Sends, logs, and backup staging.
- A Localtonet account and an installed Localtonet client on the same host for the loopback design used here.
- A password manager or offline method for storing the distinct Vaultwarden admin credential, master password, two-factor recovery codes, device token, and backup-encryption key.
Use Docker's operating-system-specific Engine installation instructions and Compose installation instructions. Package names and repository setup differ by distribution, so this guide does not substitute a generic shell pipeline for those checks.
docker --version
docker compose version
docker info
All three checks should complete without an unexpected connection or permission error. Membership in the Docker group is effectively privileged host access because a user who controls Docker can mount host paths and start privileged containers. Grant it only to administrators who require it.
Your vault master password and Vaultwarden administration password must be unique. Do not reuse either as the host login, Localtonet account password, backup key, or disk-encryption passphrase. Store two-factor recovery codes offline in a location that remains available if the vault server is down.
Install Vaultwarden with Docker Compose

The following deployment follows Vaultwarden's documented container pattern: a persistent /data mount, a loopback-only published port, a configured public domain once one is assigned, and a restart policy. Review the current Vaultwarden release notes and container-tag guidance before production use. The moving latest tag is convenient for an initial setup, but it should not be pulled automatically without review.
Create a protected project directory
Create the Compose project and persistent data directory under your administrative account. Restrict the project directory because it will contain sensitive configuration.
mkdir -p "$HOME/vaultwarden/vw-data"
cd "$HOME/vaultwarden"
chmod 700 "$HOME/vaultwarden" "$HOME/vaultwarden/vw-data"
Generate an Argon2 admin credential hash
Run Vaultwarden's hash utility and enter a strong administration password that is different from every user's master password.
docker run --rm -it vaultwarden/server:latest /vaultwarden hash
The utility returns an Argon2 PHC string beginning with $argon2. Keep the original password in your credential store. Do not use the PHC string as the password entered at /admin.
Create the protected environment file
Compose interpolation treats dollar signs specially in several contexts. In a Compose environment file, a single-quoted value is literal. Put the complete PHC value inside single quotes, keep the file out of source control, and do not repeat the variables in an environment section.
umask 077
cat > .env <<'EOF'
ADMIN_TOKEN='$argon2id$v=19$m=65540,t=3,p=4$replace_with_generated_salt$replace_with_generated_hash'
SIGNUPS_ALLOWED=true
TZ=Etc/UTC
EOF
chmod 600 .env
Replace the example with the exact generated PHC string. Keep the single quotes in the file. Do not paste a real token into terminal history, screenshots, tickets, or this article.
Create the Compose definition
The loopback binding prevents direct access through the host's LAN or public interfaces. The Localtonet client on the same host can still reach the target.
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
env_file:
- .env
volumes:
- ./vw-data:/data
ports:
- "127.0.0.1:8080:80"
Save this as compose.yaml. For a controlled production lifecycle, replace the moving image reference with a release tag or digest selected after reviewing Vaultwarden's current image guidance and release notes.
Validate and start the container
Ask Compose to validate the file, then start Vaultwarden and inspect its state and logs.
docker compose config --quiet
docker compose up -d
docker compose ps
docker compose logs --tail=100 vaultwarden
Do not publish the expanded output of docker compose config. Depending on the command and Compose version, resolved output can reveal environment values.
Test local reachability
Verify that the host can reach the loopback listener before introducing a tunnel.
curl -I http://127.0.0.1:8080/
docker compose ps
docker compose logs --tail=100 vaultwarden
An HTTP response proves that the container and host port are connected. It does not yet prove public HTTPS, certificate validity, login, synchronization, or backup recovery.
The env_file entry passes the values to the container. Repeating them as ADMIN_TOKEN=${ADMIN_TOKEN} adds another interpolation path and creates avoidable ambiguity around dollar signs. If you choose a different Compose secret-management method, test it with your exact Compose version and inspect Vaultwarden's startup warnings without printing the secret.
Create the Localtonet HTTPS access path
Localtonet exposes a service through an outbound connection from the client device to a Localtonet relay server. It does not require inbound router port forwarding, a public IP address, firewall changes, or VPN setup. For this deployment, create an HTTP tunnel whose local target is 127.0.0.1 on port 8080.
HTTP tunnels can use a generated subdomain, a custom subdomain where supported, or a custom domain. All three process types serve the target at a public HTTPS address. Do not assume a fixed hostname pattern. Use the exact URL assigned by the current dashboard.
Install and run the Localtonet client
Use the current installer for your operating system from Localtonet. Run the client on the same machine as Vaultwarden so that the local target remains on loopback. This article does not use an unverified shell installer or service-management command.
Authenticate or select the device
Use the device-specific authentication token for the Localtonet client that will run this tunnel. Treat the token as a secret. Do not place it in Compose, application logs, screenshots, or a public repository.
Open the HTTP tunnel configuration
In the dashboard, create an HTTP tunnel and choose the required process type: generated subdomain, supported custom subdomain, or custom domain. If you choose a custom domain, follow the current Localtonet DNS instructions rather than guessing the required records.
Select the device token and relay server
Select the token belonging to the client running on the Vaultwarden host, then select an available Localtonet relay server or region shown by the current product. Do not hardcode a server code from an older tutorial.
Set the local target
Enter 127.0.0.1 as the local IP address and 8080 as the local port. This must match the loopback port published in compose.yaml.
Create and explicitly start the tunnel
Save the tunnel configuration, then press Start. Creating a tunnel does not make it run. Record the exact assigned public HTTPS URL only after the dashboard shows the tunnel running.
Set Vaultwarden's public domain
Add the exact assigned URL to .env. Preserve the existing single-quoted admin hash and use only one DOMAIN line.
cd "$HOME/vaultwarden"
printf '\nDOMAIN=https://your-exact-assigned-hostname\n' >> .env
chmod 600 .env
docker compose up -d
docker compose logs --tail=100 vaultwarden
Replace the placeholder with the assigned URL. Recreating the container with docker compose up -d applies the updated environment.
Vaultwarden must be running, the selected Localtonet client must remain connected, and the tunnel must remain started. Relay availability and the timeout or availability behavior of the applicable Localtonet plan also affect continuous access. For example, current free-plan behavior can include a tunnel timeout, while other plans may differ. Confirm current plan details before treating this as an always-on production endpoint.
Access controls and client compatibility
Where your selected Localtonet configuration supports them, IP allowlisting and related access controls can reduce exposure. Apply least privilege and test every required client network. A narrow home IP allowlist, for example, may block a phone when it switches to mobile data.
Be careful with an additional HTTP Basic Authentication, SSO, or interactive authentication layer in front of Vaultwarden. A browser can display an extra login prompt, but a Bitwarden native client may not know how to complete that challenge for API and synchronization requests. Do not enable an outer authentication layer until you have tested login, synchronization, attachments, Sends, and every required client.
First login and security hardening
Create the initial account and close registration
Open the assigned HTTPS URL and confirm that the browser reports a valid certificate for the exact hostname. Create the intended account with a unique master password. After all authorized initial accounts are created, change SIGNUPS_ALLOWED to false and recreate the container:
cd "$HOME/vaultwarden"
sed -i 's/^SIGNUPS_ALLOWED=.*/SIGNUPS_ALLOWED=false/' .env
docker compose up -d
docker compose logs --tail=100 vaultwarden
Then use a private browser window to confirm that public registration is no longer available. Do not rely only on the value in the file. A persisted administration configuration can affect effective settings, so test the actual public behavior after every security change.
Protect or disable the administration panel
The administration endpoint controls server settings and user administration. At /admin, enter the original administration password used to generate the Argon2 hash, not the PHC string stored in .env.
Use an administration password unrelated to every vault master password. Do not expose the administration endpoint more broadly than necessary. If you do not need ongoing web administration, consider disabling it by removing the admin token after completing required configuration. If you retain it, combine a strong credential with compatible tunnel restrictions and monitor access attempts.
Enable two-factor authentication and preserve recovery material
Enable a supported two-step login method for each account. Vaultwarden currently implements multiple authentication options, including authenticator-based codes and FIDO2 WebAuthn. Availability and behavior can vary by client and configuration, so enroll and test at least two client sessions before declaring the setup complete.
Store recovery codes outside the vault in a physically or cryptographically protected location. A recovery code kept only inside the vault is unavailable precisely when it is most needed. Do not assume the server administrator can decrypt a user's vault or reset a forgotten master password. Some organization and client recovery workflows may exist when configured in advance, but they are not a universal operator override.
Secure the host and secrets
.env, Localtonet device tokens, SMTP credentials, backup keys, and recovery codes out of repositories, logs, shell history, and screenshots.
127.0.0.1 unless a documented architecture requires a broader listener.
Why this guide does not publish a Fail2Ban sidecar
A working Fail2Ban design requires the application log to contain the real client IP, a verified trusted-proxy configuration, and a ban action that can actually block the traffic path. With a relay or reverse proxy in front of Vaultwarden, the observed address may be a relay or proxy address instead of the user. A mistaken filter can ban infrastructure, ban nothing useful, or create a false sense of protection.
The previous configuration also granted a floating third-party container NET_ADMIN and NET_RAW capabilities. Those privileges materially increase its impact if compromised. This revision removes that configuration. Do not deploy Fail2Ban for this architecture until you have verified real client-IP propagation end to end, documented which proxy addresses are trusted, tested the exact Vaultwarden log format, pinned and reviewed the filtering software, and proved that a test ban blocks only the intended client.
Connect and test Bitwarden clients
Install an official Bitwarden client from its normal distribution channel. Before logging in, select the client's self-hosted server option and enter the exact Localtonet HTTPS URL. Labels and menu placement can change between browser, desktop, iOS, and Android releases, so use the current self-hosted server field rather than relying on a fixed sequence of UI labels.
Set the self-hosted URL
Enter the assigned HTTPS origin without adding /admin, an API path, or a trailing private route. Confirm the client accepts the certificate without a trust warning.
Log in and complete two-step login
Use the vault account email and master password, not the Vaultwarden administration password. Complete the configured second factor.
Test synchronization in both directions
Create a harmless test item in one client, synchronize another client, edit the item there, and verify that the first client receives the change.
Test required features
If you depend on organizations, collections, attachments, Sends, WebAuthn, emergency access, or another advanced workflow, test that feature with the exact client versions you will deploy.
If a newly updated client behaves differently, check Vaultwarden's current compatibility discussions before changing security settings. โCompatible with official clientsโ does not guarantee that every new official feature is available immediately.
Verify the complete deployment
A successful container start is only the first check. Complete the following acceptance tests before importing real credentials or depending on the service.
| Test | How to perform it | Expected result |
|---|---|---|
| Local reachability | Request http://127.0.0.1:8080 from the host and inspect the container state. |
Vaultwarden responds locally and the container remains healthy enough to serve requests. |
| Public HTTPS and certificate | Open the assigned URL from a separate network and inspect the certificate hostname, validity period, and trust chain. | No warning, expiry error, hostname mismatch, or incomplete-chain error appears. |
| Account login | Log in through the web vault and at least one native client. | Authentication and the configured second factor complete successfully. |
| Synchronization | Create and edit a harmless test item across two clients. | Both clients receive the changes after synchronization. |
| Registration closure | Use a private browser session after setting SIGNUPS_ALLOWED=false. |
An unauthenticated visitor cannot create an unrestricted new account. |
| Restart persistence | Restart the container, then perform a planned host reboot during a maintenance window. | The original test vault remains present and the container returns without creating an empty data directory. |
| Tunnel state | Check that the selected client is connected and the tunnel is started after restart. | The assigned URL returns only after all required components are active. |
| Failure behavior | Separately stop the tunnel, disconnect the Localtonet client, and stop Vaultwarden, restoring each component after the test. | Remote access fails as expected in every case and returns only when the missing dependency is restored. |
| Restore test | Restore a backup into an isolated test directory or host. | Users can log in and verify test records, attachments, Sends, settings, and required keys without altering production. |
Raspberry Pi deployment and safe external storage
Vaultwarden publishes container images intended for self-hosted use, but support for a particular Raspberry Pi depends on the current image architecture, the installed 64-bit operating system, Docker support, and available storage. Check the host architecture before pulling the image:
uname -m
docker info --format '{{.Architecture}}'
lsblk -f
An ARM64 system commonly reports aarch64. Do not rely on old memory figures or assume that every Pi model and operating-system combination is supported. Verify the current Vaultwarden image tags and Docker platform support for your hardware.
Move existing data to an external drive safely
Never assume the external disk is /dev/sda1, never assume its filesystem is ext4, and never edit /etc/fstab before validating the correct filesystem and UUID. Changing the Compose mount without copying existing data can make an established vault appear empty because Vaultwarden starts against a new blank directory.
Create and verify a current backup
Complete the backup procedure in the next section and verify that its SQLite snapshot opens. Do not begin a storage migration with the only copy of the vault on the source card.
Identify the correct filesystem
Connect the external drive and inspect device names, filesystem types, labels, UUIDs, sizes, and existing mount points.
lsblk -f
sudo blkid
findmnt
Match the device by size, label, and UUID. Stop if the identity is ambiguous.
Stop Vaultwarden
Prevent database and attachment changes while migrating the complete data directory.
cd "$HOME/vaultwarden"
docker compose down
Mount and validate the destination
Create a mount point, mount the identified filesystem using the correct device or UUID, and verify it with findmnt. Substitute the verified device for the placeholder.
sudo mkdir -p /mnt/vault-storage
sudo mount /dev/verified-partition /mnt/vault-storage
findmnt /mnt/vault-storage
df -h /mnt/vault-storage
Copy data while preserving metadata
Use rsync with archive semantics. A trailing slash on the source copies the directory contents into the destination.
sudo mkdir -p /mnt/vault-storage/vw-data
sudo rsync -aHAX --numeric-ids "$HOME/vaultwarden/vw-data/" /mnt/vault-storage/vw-data/
sudo rsync -aHAXn --delete --numeric-ids "$HOME/vaultwarden/vw-data/" /mnt/vault-storage/vw-data/
The dry run should show no unexplained differences. Inspect ownership and permissions with ls -la on both directories.
Add and test the persistent mount
Back up /etc/fstab, then add an entry using the verified UUID and filesystem type. The exact options depend on that filesystem and host. Test the file before rebooting.
sudo cp /etc/fstab /etc/fstab.pre-vaultwarden
sudoedit /etc/fstab
sudo umount /mnt/vault-storage
sudo mount -a
findmnt /mnt/vault-storage
If mount -a reports an error or the expected filesystem is absent, restore the backup and correct the entry before proceeding.
Change the Compose mount only after validation
Replace ./vw-data:/data with the verified destination path:
volumes:
- /mnt/vault-storage/vw-data:/data
Do not delete or rename the original source directory yet.
Restart and confirm the original vault
Start Vaultwarden, inspect logs, and log in to verify known records and attachments.
docker compose up -d
docker compose ps
docker compose logs --tail=100 vaultwarden
Retain the original data and verified backup until the new mount has survived a planned reboot and a complete application check.
Back up and restore Vaultwarden correctly

Backups must cover more than db.sqlite3. A recoverable Vaultwarden set includes a consistent SQLite snapshot, attachments, Sends, signing keys, persisted configuration, and the secrets needed to reconstruct the deployment. The environment file, SMTP credentials, device tokens, and backup keys are sensitive and should not be copied into an unencrypted backup directory.
| Component | Purpose | Recovery treatment |
|---|---|---|
db.sqlite3 |
Vault records, users, organizations, and related application state. | Create a consistent SQLite snapshot rather than copying only the live main database file. |
attachments/ |
Files attached to vault items. | Copy recursively and restore with the database. |
sends/ |
Stored files associated with Sends. | Copy recursively if present. |
rsa_key* |
Server signing-key material. | Preserve all matching files and their permissions. |
config.json |
Settings persisted through the administration interface. | Restore deliberately because these settings can override or interact with environment configuration. |
.env and deployment definition |
Admin hash, public domain, signup policy, and other deployment settings. | Store only in an encrypted secret backup with tightly controlled access. |
Install the backup prerequisites
The host needs the sqlite3 command-line client and rsync. Install those packages from your operating system's signed package repository. Verify them before scheduling a backup:
sqlite3 --version
rsync --version
Create a consistent backup set
The SQLite .backup command creates a consistent database snapshot while Vaultwarden is running. The other directories can still change during a live copy, so schedule backups during a quiet period. For the strongest cross-file consistency, stop Vaultwarden briefly before copying attachments, Sends, keys, and configuration, or use a storage snapshot mechanism whose consistency you have tested.
#!/bin/sh
set -eu
umask 077
PROJECT="$HOME/vaultwarden"
DATA="$PROJECT/vw-data"
ROOT="$HOME/vaultwarden-backups"
STAMP="$(date -u +%Y%m%dT%H%M%SZ)"
DEST="$ROOT/$STAMP"
command -v sqlite3 >/dev/null
command -v rsync >/dev/null
mkdir -p "$DEST"
sqlite3 "$DATA/db.sqlite3" ".timeout 10000" ".backup '$DEST/db.sqlite3'"
for directory in attachments sends; do
if [ -d "$DATA/$directory" ]; then
rsync -aH --numeric-ids "$DATA/$directory/" "$DEST/$directory/"
fi
done
if [ -f "$DATA/config.json" ]; then
cp -p "$DATA/config.json" "$DEST/config.json"
fi
for key in "$DATA"/rsa_key*; do
if [ -e "$key" ]; then
cp -p "$key" "$DEST/"
fi
done
sqlite3 "$DEST/db.sqlite3" "PRAGMA integrity_check;"
find "$DEST" -maxdepth 2 -type f -print
echo "Backup staging set created at: $DEST"
The integrity check must report ok. Save this script outside the public service path, make it executable only by its owner, and run it manually before scheduling it.
Encrypt the backup before copying it off-host, protect the encryption key separately, and retain at least one copy that is not continuously writable by the Vaultwarden host. Backups can expose account metadata, server configuration, signing keys, attachments, and secrets. Encrypted vault records can also become an offline password-guessing target. The absence of a plaintext master password does not make a backup safe to disclose.
Back up .env, compose.yaml, Localtonet recovery information, and any SMTP configuration only inside the encrypted archive or secret-management system. Do not mix unencrypted secret copies into the ordinary backup staging directory. Define retention based on your recovery objectives, available storage, and need to recover from delayed corruption or accidental deletion.
Restore into an isolated test location
A backup is not proven until it has been restored. The safest recurring test uses an isolated host or a separate project directory and port. Never point a test container at the production data directory.
Verify and decrypt the selected backup
Confirm the archive's integrity using the mechanism provided by your encryption tool. Extract it into a protected temporary directory and verify the SQLite snapshot:
sqlite3 /protected/restore-source/db.sqlite3 "PRAGMA integrity_check;"
Stop the target Vaultwarden instance
For a production recovery, stop the target container before replacing any file. Preserve the failed data directory for investigation instead of deleting it immediately.
cd "$HOME/vaultwarden"
docker compose down
mv vw-data "vw-data.before-restore.$(date -u +%Y%m%dT%H%M%SZ)"
mkdir vw-data
chmod 700 vw-data
Restore every required component
Copy the database, attachments, Sends, keys, and configuration while preserving metadata.
cp -p /protected/restore-source/db.sqlite3 "$HOME/vaultwarden/vw-data/"
rsync -aH --numeric-ids /protected/restore-source/attachments/ "$HOME/vaultwarden/vw-data/attachments/"
rsync -aH --numeric-ids /protected/restore-source/sends/ "$HOME/vaultwarden/vw-data/sends/"
cp -p /protected/restore-source/config.json "$HOME/vaultwarden/vw-data/"
cp -p /protected/restore-source/rsa_key* "$HOME/vaultwarden/vw-data/"
Skip only components that were genuinely absent from the source. Restore the protected environment and deployment secrets through your encrypted secret-recovery process.
Check ownership and permissions
Compare the restored directory with the preserved pre-restore directory or a known-good deployment. Do not apply a guessed numeric user ID. The required ownership can depend on the image and host setup.
ls -la "$HOME/vaultwarden/vw-data"
find "$HOME/vaultwarden/vw-data" -maxdepth 2 -printf '%M %u:%g %p\n'
Start and validate the restored vault
Start the target, inspect logs, then test login, synchronization, attachments, Sends, settings, and account records.
docker compose up -d
docker compose ps
docker compose logs --tail=200 vaultwarden
For a test restore, keep it isolated from production clients and mail delivery. Record the date, selected backup, result, and any manual recovery step.
Migrate an existing Bitwarden vault carefully
Review the current export choices in the Bitwarden client before creating a migration file. Encrypted JSON exports can use different protection models. A password-protected encrypted export is designed to be unlocked with the export password and is the appropriate encrypted form when portability is required. An account-restricted encrypted export is tied to the originating account and is not interchangeable with a portable password-protected export.
If the target importer does not support the selected encrypted format, a plaintext export may be required. Perform that operation only on a trusted, patched device, keep the file on encrypted storage, disconnect unnecessary synchronization or backup software, import it promptly, and securely remove temporary copies afterward. Confirm the import before deleting the source account.
Vault exports may not include every service object. Attachments and Sends require separate handling, and organization data can have its own export and import rules. Compare item counts and manually verify representative logins, secure notes, identities, cards, custom fields, organization collections, attachments, and two-factor records. There is no reliable universal migration-duration estimate.
Routine operation, updates, and rollback planning
Daily and weekly checks
- Confirm the Vaultwarden container is running and review new startup or authentication warnings.
- Confirm the Localtonet device is connected and the tunnel is started.
- Test the public URL and certificate before certificate expiry becomes urgent.
- Confirm scheduled backups completed, were encrypted, and reached the off-host destination.
- Monitor disk capacity for the data directory, Docker storage, logs, attachments, and backup staging.
- Review account enrollment, administration access, and whether public registration remains closed.
Review updates before applying them
A moving image tag changes when pulled. Before an update, read Vaultwarden release notes, check client-compatibility reports relevant to your environment, create and verify a fresh backup, and record the current image identifier:
cd "$HOME/vaultwarden"
docker compose images
docker image inspect vaultwarden/server:latest --format '{{index .RepoDigests 0}}'
docker compose logs --tail=100 vaultwarden
During a maintenance window, pull the reviewed image, recreate the service, and inspect logs:
docker compose pull vaultwarden
docker compose up -d vaultwarden
docker compose ps
docker compose logs --tail=200 vaultwarden
Then repeat certificate, login, synchronization, registration, and attachment tests. Do not automate unattended pulls for a password manager unless your organization has a tested update, monitoring, and rollback process.
Plan rollback before updating
Retain the previous image reference and the pre-update backup. Database migrations can make a simple image downgrade unsafe. If the new release changes the database in an incompatible way, rollback may require stopping the service, restoring the complete pre-update data set and required secrets, and starting the previously tested image. Document that process before the maintenance window.
Understand outage behavior
| Component stopped | Public behavior | Recovery action |
|---|---|---|
| Vaultwarden container | The tunnel can remain present, but the local target cannot serve vault requests. | Inspect Compose state and Vaultwarden logs, correct the application or storage issue, then retest login and sync. |
| Localtonet client | The relay cannot reach the local target through that device. | Restore the selected client connection and protect its device token. |
| HTTP tunnel | The assigned public endpoint is unavailable even if Vaultwarden is healthy locally. | Start the existing tunnel and verify its selected device and relay server. |
| Host or storage | Both Vaultwarden and the local tunnel client may disappear. An unmounted data disk can also cause an empty directory or startup failure. | Validate filesystems and mounts before starting containers, then confirm the original vault is present. |
Evidence-based troubleshooting
| Symptom | Checks | Safe response |
|---|---|---|
| Public URL is unavailable | Test 127.0.0.1:8080, check the container, confirm the selected Localtonet client is connected, and confirm the tunnel is started. |
Restore the first failed dependency. Creating a new tunnel is not necessary if the existing tunnel is merely stopped. |
| Certificate warning or native-client trust error | Inspect hostname, validity dates, certificate chain, device clock, and the exact URL configured in the client. | Correct the certificate or URL. Never train users to bypass a password-manager certificate warning. |
| Administration login fails | Confirm that you entered the original admin password rather than the PHC hash. Review logs for interpolation or persisted-configuration warnings. | Correct the effective configuration without printing the secret. Remember that config.json can contain settings saved through the admin interface. |
| Argon2 value is malformed | Confirm the complete generated value is single-quoted in .env, the file has no accidental line break, and Compose is using only env_file. |
Regenerate the hash if necessary. Avoid resolved Compose output in tickets because it can expose secrets. |
| Client login works but synchronization fails | Verify the self-hosted URL, public certificate, tunnel access controls, outer authentication layers, and compatibility of the exact client version. | Remove an incompatible outer challenge only after reviewing exposure. Test the bundled web vault and consult Vaultwarden compatibility reports. |
| Vault appears empty after moving storage | Stop the container and use findmnt to confirm the external filesystem is mounted at the expected path. Compare the old and new data directories. |
Do not create new records. Restore the correct mount or revert the Compose path, then verify the original database and attachments. |
Container cannot write to /data |
Inspect mount state, filesystem permissions, ownership, read-only flags, and logs. | Compare with the known-good source. Do not recursively assign a guessed user ID or grant world-writable permissions. |
| Backup database fails integrity check | Confirm the source filesystem is healthy, sufficient space exists, and the snapshot was made with SQLite's backup command. | Preserve the failed set for investigation, create a new snapshot, and do not delete the last known-good off-host backup. |
| Attachments are missing after restore | Check whether attachments/ was included, restored to the correct data directory, and readable by the container. |
Stop the service, restore the matching attachments directory from the same backup set, verify permissions, and retest. |
Frequently asked questions
Is Vaultwarden fully compatible with every Bitwarden client?
No permanent universal guarantee should be assumed. Vaultwarden describes itself as a nearly complete implementation of the Bitwarden Client API and is designed for use with official clients. New client releases and features can introduce temporary or lasting differences. Test the exact browser, desktop, mobile, and feature combinations you require.
Can the server administrator recover a forgotten master password?
Do not assume that the operator can decrypt a user's vault or simply reveal a forgotten master password. Recovery can depend on client or organization features configured before the loss, and those workflows are not a universal administrative override. Preserve the master password and two-factor recovery material securely outside the server.
Are Vaultwarden backups safe because vault records are encrypted?
No. Backups remain sensitive. They can contain encrypted records, account metadata, attachments, Sends, signing keys, administration configuration, and deployment secrets. They can also provide material for offline password guessing. Encrypt backups, keep a protected off-host copy, control the encryption key separately, and test restoration.
Do I need to own a domain?
No. A Localtonet HTTP tunnel can provide an assigned public HTTPS address using a generated subdomain. Custom subdomains and custom domains may also be available depending on current product and plan behavior. Use the exact URL displayed by the dashboard and do not assume a fixed hostname pattern.
Does Localtonet make Vaultwarden permanently available?
Availability depends on more than creating a tunnel. Vaultwarden must be running, the selected Localtonet client must remain connected, and the tunnel must be started. Relay availability and plan-specific timeout or availability behavior also apply. Confirm current plan terms for an always-on deployment.
Is the connection HTTPS all the way into Vaultwarden?
Not in this tutorial. The remote client uses HTTPS to the public endpoint, and traffic travels through the outbound Localtonet tunnel. The Localtonet client then connects to Vaultwarden over HTTP at 127.0.0.1:8080. Keeping both processes on the same trusted host confines that final HTTP hop to loopback.
Can Vaultwarden run on a Raspberry Pi?
It can run on compatible Raspberry Pi hardware when the current Vaultwarden image supports the architecture and the host uses a Docker-supported operating system. Use a maintained 64-bit installation, verify the architecture and image support, provide reliable storage, and avoid relying on old model-specific memory claims.
Which encrypted export should I use for migration?
Use a password-protected encrypted export when you need a portable encrypted file and the target importer supports it. An account-restricted encrypted export is tied to the originating account and should not be assumed portable to another server. Attachments, Sends, and organization data may require separate migration steps.
Publish your loopback-only Vaultwarden through Localtonet
Install Vaultwarden, verify it locally, then create an HTTP tunnel to 127.0.0.1:8080. Keep the device connected, explicitly start the tunnel, validate the assigned HTTPS certificate, and complete the security and restore tests before importing production credentials.