Passwords are among the most sensitive services you can move into a homelab. A password manager must remain available, synchronize reliably across devices, use HTTPS correctly, and have backups that can actually be restored.
Vaultwarden provides a lightweight, unofficial implementation of the Bitwarden client API. It works with the official Bitwarden browser extensions, mobile apps, desktop clients, and command-line client while using far fewer resources than the full official Bitwarden server stack.
That makes running Vaultwarden on ARM64 a practical fit for a Turing Pi 2.5 cluster. A single RK1 node can run Vaultwarden alongside other lightweight services without needing a dedicated compute module. The important part is not CPU or memory capacity, but deploying the service with persistent storage, HTTPS, restricted registrations, a protected admin token, and a tested backup process.
In this guide, you will deploy Vaultwarden on an ARM64-based RK1 using Docker Compose, store its data on NVMe, place it behind Caddy, connect official Bitwarden clients, disable public registration, and create a backup that includes the database and file attachments.
Part 1: How the Vaultwarden Deployment Works
The deployment has four main layers:
Bitwarden browser, mobile, and desktop clients
|
HTTPS
|
Caddy
|
Vaultwarden container
|
/mnt/nvme/vaultwarden/data
|
SQLite database, attachments,
server keys, and configuration
Bitwarden clients connect to Vaultwarden through HTTPS. Caddy terminates the encrypted connection and forwards requests to the Vaultwarden container over a private Docker network.
Vaultwarden stores its persistent data inside /data, which is mapped to /mnt/nvme/vaultwarden/data on the RK1. This directory contains the SQLite database, attachments, Send files, server keys, and configuration files.
SQLite is appropriate for a personal, family, or small-team deployment. It keeps the stack simple and avoids adding a separate PostgreSQL or MariaDB container. The database is only one part of a complete backup, however, because attachments and other server files are stored separately inside the same data directory.
Vaultwarden’s web vault requires HTTPS because browsers expose the necessary Web Crypto APIs only in a secure context. The Vaultwarden project also recommends placing the service behind a reverse proxy instead of exposing its internal HTTP endpoint directly.
This guide pins Vaultwarden to version 1.37.1 so the deployment remains reproducible. Before deploying the guide later, check the Vaultwarden releases page and update the version if necessary, because official Bitwarden clients evolve independently and may eventually require a newer compatible server release.
Part 2: Prerequisites
You need:
- A Turing Pi 2.5 with at least one RK1 running Ubuntu Server ARM64
- SSH access to the RK1
- Docker Engine and Docker Compose
- Persistent storage for Vaultwarden data
- A hostname such as
vault.home.com - A reverse proxy capable of providing HTTPS
Vaultwarden is lightweight enough for an 8GB RK1. The 16GB and 32GB versions provide additional capacity for other services but do not materially change this deployment.
This guide stores Vaultwarden data under /mnt/nvme, but NVMe is not required. You can use any persistent storage path available on your RK1. Replace /mnt/nvme in the commands and Compose file with your own path if it differs.
Avoid storing Vaultwarden data only inside the container or on temporary storage. Its database, attachments, server keys, and configuration must survive container recreation and system restarts.
This guide uses Caddy as the reverse proxy. If you have not configured it yet, follow the Homelab Reverse Proxy with Caddy on ARM64 guide first. It covers the Caddy deployment, HTTPS, DNS, and the shared proxynet Docker network used throughout this article.
You can also use Nginx, Traefik, or another reverse proxy. In that case, replace the Caddy-specific configuration later in this guide with the equivalent configuration for your chosen proxy.
Part 3: Prepare Persistent Storage
Create a dedicated deployment directory, persistent data directory, and backup directory:
sudo mkdir -p /opt/vaultwarden
sudo mkdir -p /mnt/nvme/vaultwarden/data
sudo mkdir -p /mnt/nvme/backups/vaultwarden
sudo chown -R "$USER":"$USER" /opt/vaultwarden
sudo chown -R "$USER":"$USER" /mnt/nvme/vaultwarden
sudo chown -R "$USER":"$USER" /mnt/nvme/backups/vaultwarden
chmod 700 /mnt/nvme/backups/vaultwarden
cd /opt/vaultwarden
This guide uses /mnt/nvme for persistent storage. Replace that path throughout the article if your RK1 uses a different mount point.
Vaultwarden will store its persistent application data in:
/mnt/nvme/vaultwarden/data
This directory must remain available across container recreation, upgrades, and system restarts. Do not store the database only inside the container, because removing or recreating the container would also remove any non-persistent data.
Generate a Hashed Admin Token
The /admin page can manage users, invitations, diagnostics, and server settings. Protect it with a long, unique password and store only its Argon2id hash in the deployment configuration.
Generate the hash using the same Vaultwarden image used by this guide:
docker run --rm -it \
vaultwarden/server:1.37.1 \
/vaultwarden hash
Enter a strong admin password twice. Vaultwarden will print a hash beginning with:
$argon2id$
Create the environment file:
nano /opt/vaultwarden/.env
Add the Vaultwarden hostname used by this guide:
DOMAIN=https://vault.home.arpa
Next, copy the complete ADMIN_TOKEN=... line printed by the hash-generation command and place it directly below the domain. The completed file should look like this:
DOMAIN=https://vault.home.arpa
ADMIN_TOKEN='$argon2id$v=19$m=65540,t=3,p=4$REPLACE_WITH_THE_COMPLETE_GENERATED_HASH'
Do not remove the single quotes around the Argon2id hash. They prevent Docker Compose from interpreting the dollar signs as environment-variable references.
Save the file, exit Nano, and restrict access to it:
chmod 600 /opt/vaultwarden/.env
The admin password and your personal vault master password must be different. The admin password protects the Vaultwarden server panel, while the master password encrypts and unlocks your personal vault.
Part 4: Deploy Vaultwarden with Docker Compose
Create the Compose file:
nano /opt/vaultwarden/compose.yaml
Add:
services:
vaultwarden:
image: vaultwarden/server:1.37.1
container_name: vaultwarden
restart: unless-stopped
environment:
DOMAIN: ${DOMAIN}
ADMIN_TOKEN: ${ADMIN_TOKEN}
SIGNUPS_ALLOWED: "true"
volumes:
- /mnt/nvme/vaultwarden/data:/data
networks:
- proxynet
networks:
proxynet:
external: true
This configuration reads DOMAIN and ADMIN_TOKEN from the .env file created earlier.
Public registration is temporarily enabled so you can create the first account. It will be disabled immediately afterward.
Validate the Compose configuration:
cd /opt/vaultwarden
docker compose config --quiet
No output means the Compose file is valid. If Docker reports a YAML or environment-variable error, correct it before starting the container.
Pull the pinned Vaultwarden image and start the service:
docker compose pull
docker compose up -d
Check the container status and recent logs:
docker compose ps
docker logs --tail 100 vaultwarden
The container should show as running, and the logs should not contain database, permission, or configuration errors.
Confirm that Docker selected the ARM64 image:
docker image inspect vaultwarden/server:1.37.1 \
--format 'Architecture={{.Architecture}} OS={{.Os}}'
Expected output:
Architecture=arm64 OS=linux
Confirm that Vaultwarden joined the shared reverse-proxy network:
docker network inspect proxynet \
--format '{{range $id, $container := .Containers}}{{$container.Name}}{{"\n"}}{{end}}'
Both caddy and vaultwarden should appear.
The Compose file does not publish a host port. Vaultwarden is reachable only by other containers connected to proxynet, allowing Caddy to proxy requests without exposing Vaultwarden’s unencrypted HTTP endpoint directly on the RK1.
Part 5: Add HTTPS with Caddy
Vaultwarden’s web vault should be accessed over HTTPS. In this deployment, Caddy handles TLS and proxies requests to Vaultwarden over the shared proxynet Docker network.
This guide uses the private hostname:
vault.home.arpa
Make sure it resolves to the RK1 running Caddy and Vaultwarden. In this setup, Pi-hole provides the local DNS record:
vault.home.arpa → 192.168.x.x
Replace 192.168.x.x with the IP address of your RK1.
You can verify resolution from another device on the network with:
dig vault.home.arpa
The returned address should match the RK1.
Add Vaultwarden to the Caddyfile
Open the Caddy configuration:
nano ~/docker/caddy/conf/Caddyfile
Add:
vault.home.arpa {
tls internal
reverse_proxy vaultwarden:80
}
The reverse_proxy vaultwarden:80 directive works because both Caddy and Vaultwarden are connected to the shared proxynet Docker network on the same Docker host. Vaultwarden does not need to publish its HTTP port directly on the RK1. If Caddy and Vaultwarden run on different RK1 nodes, the proxy must instead use a network address that is reachable from the Caddy node.
The hostname must match the value already configured in /opt/vaultwarden/.env:
DOMAIN=https://vault.home.arpa
Validate and Reload Caddy
Validate the Caddy configuration before applying it:
docker exec caddy \
caddy validate --config /etc/caddy/Caddyfile
A valid configuration ends with:
Valid configuration
A warning that the Caddyfile is not formatted does not prevent it from working. You can optionally format it with:
docker exec caddy \
caddy fmt --overwrite /etc/caddy/Caddyfile
Reload Caddy:
docker exec caddy \
caddy reload --config /etc/caddy/Caddyfile
If the site does not load, check the recent Caddy logs:
docker logs --tail 50 caddy
Open the Vaultwarden Web Vault
Open:
https://vault.home.arpa
If DNS, Caddy, and Vaultwarden are configured correctly, the Vaultwarden login page should load through Caddy.
You can also verify the proxy path from another machine:
curl -vk https://vault.home.arpa
The -k option skips certificate verification, so this test checks DNS, TLS connectivity, and proxy routing rather than certificate trust. A successful request should connect to the RK1 on port 443 and return an HTTP 200 response from Vaultwarden through Caddy.
Trust Caddy’s Internal Certificate Authority
The tls internal directive tells Caddy to issue the certificate from its own local certificate authority instead of requesting one from a public CA.
Because of this, browsers and Bitwarden clients will initially show a certificate warning unless the device trusts Caddy’s root certificate.
Install Caddy’s root certificate on every browser, desktop, and mobile device that will connect to Vaultwarden. The Homelab Reverse Proxy with Caddy on ARM64 guide covers locating and installing the Caddy root certificate.
Do not ignore certificate warnings for normal Vaultwarden use. The web vault and Bitwarden clients should trust the certificate before you begin storing real credentials.
After installing the root certificate, you can verify normal certificate validation from a machine that trusts it:
curl -v https://vault.home.arpa
If you are using a public domain instead of a private .home.arpa hostname, replace vault.home.arpa with your domain and remove:
tls internal
Caddy can then request a publicly trusted certificate automatically, provided the hostname resolves correctly and the required ports are reachable.
Verify WebSocket Notifications
Vaultwarden uses WebSockets to provide real-time notifications to browser extensions, the web vault, and desktop clients. Android and iOS use native push notifications instead; mobile push configuration is outside the scope of this local deployment. Current Vaultwarden releases use the same HTTP endpoint as the rest of the service, and Caddy handles WebSocket proxying automatically.
You do not need a separate port 3012 proxy or the older WEBSOCKET_ENABLED configuration used by some previous Vaultwarden deployments.
After signing in, open the browser developer tools, select the Network tab, and filter for:
WS
Refresh the page and look for a connection to:
/notifications/hub
A successful WebSocket connection should return:
101 Switching Protocols
You can also test this from two browser sessions. Create or rename a temporary vault item in one session and confirm that the change appears in the other without manually refreshing the page.
Part 6: Create the First Account and Disable Signups
Open:
https://vault.home.arpa
and create your first Vaultwarden account.
Use a unique email address and a long master password that is different from the admin password configured earlier.
After signing in successfully, Vaultwarden may prompt you to install the Bitwarden browser extension. You can install it now, or skip the prompt and continue using the web vault. The extension can be added later and pointed at the same self-hosted URL.
Before importing real credentials, sign out once and confirm that you can sign back in successfully.
Disable Public Registration
Public signups were enabled only so the first account could be created.
Open the Compose file:
nano /opt/vaultwarden/compose.yaml
Change:
SIGNUPS_ALLOWED: "true"
to:
SIGNUPS_ALLOWED: "false"
Apply the change:
cd /opt/vaultwarden
docker compose up -d
Confirm the running value:
docker exec vaultwarden printenv SIGNUPS_ALLOWED
Expected output:
false
Open the web vault in a private browser window and confirm that account creation is no longer available.
Disabling signups does not affect the account you already created. Additional users can still be invited through the Vaultwarden admin panel.
Access the Admin Panel
The admin interface is available at:
https://vault.home.arpa/admin
Use the admin password configured earlier.
The admin panel can be used to manage users, invitations, diagnostics, and server settings. If you later expose Vaultwarden to the internet, protect /admin with an additional access restriction such as a VPN or reverse-proxy policy.
Part 7: Connect Bitwarden Clients
Vaultwarden works with the official Bitwarden browser extension, mobile apps, and desktop clients.
For this deployment, use the same self-hosted server URL everywhere:
https://vault.home.arpa
Because this setup uses Caddy’s internal certificate authority, each device must trust Caddy’s root certificate before Bitwarden can connect normally.
Browser Extension
Install the official Bitwarden browser extension.
On the login screen, open the Accessing or server selector near the bottom and choose:
Self-hosted
Enter:
https://vault.home.arpa
as the Server URL, save the configuration, and log in with the Vaultwarden account created earlier.
Once connected, the extension should open your self-hosted vault instead of Bitwarden Cloud.
Android or iOS
Install the official Bitwarden mobile app.
From the login screen:
- Open the server or region selector.
- Choose Self-hosted.
- Enter:
https://vault.home.arpa
in the Server URL field.
4. Save the configuration and log in.
The app may also show a Custom Environment section with fields such as:
- Web Vault Server URL
- API Server URL
- Identity Server URL
- Icons Server URL
- Client certificate or mTLS options
Leave these fields unchanged for this deployment. The single Server URL is enough.
If the App Cannot Verify the Certificate
Because vault.home.arpa uses Caddy’s internal CA, the mobile app may initially report that it cannot verify the server certificate.
Export Caddy’s root certificate on the RK1:
docker exec caddy \
cat /data/caddy/pki/authorities/local/root.crt \
> ~/caddy-root.crt
Confirm the file exists:
ls -lh ~/caddy-root.crt
Transfer caddy-root.crt to the phone and install it as a trusted CA certificate.
On Android, the option is usually under:
Settings
→ Security & privacy
→ More security settings
→ Encryption & credentials
→ Install a certificate
→ CA certificate
The exact menu names vary by device.
After installing the certificate, fully close and reopen Bitwarden, choose Self-hosted, and log in again using:
https://vault.home.arpa
The app should now connect normally.
Desktop Client
Open the Bitwarden desktop application and select Self-hosted from the server selector on the login screen.
Enter:
https://vault.home.arpa
as the Server URL, save the configuration, and log in.
As with the mobile app, the single Server URL is sufficient. Separate API, identity, icons, or other service URLs are not required for this deployment.
Test Synchronization
Create a temporary login item in one client:
Name: Vaultwarden Sync Test
Username: test-user
Password: generate a random value
Open another connected client and synchronize the vault.
Confirm that the test item appears, then edit it from the second client and verify that the change appears in the first.
Delete the test item when finished.
Before migrating an existing password vault, confirm that synchronization works between at least two clients and verify the backup contents later in this guide.
Part 8: Security Hardening
Before storing important credentials, finish a few basic security checks.
Enable Two-Step Login
In the web vault, go to:
Settings
→ Security
→ Two-step login
Under Providers, choose one of the available methods.
The simplest option is Authenticator app:
- Click Manage next to Authenticator app.
- Scan the QR code with your authenticator app.
- Enter the verification code to confirm setup.
- Save the recovery code shown by Vaultwarden somewhere outside the vault.
You can also configure a Passkey or FIDO2-compatible security key from the same Two-step login page.
Do not store the only copy of your recovery code inside Vaultwarden. Keep it somewhere you can still access if you are locked out of the vault.
Keep Signups Disabled
Leave:
SIGNUPS_ALLOWED: "false"
enabled during normal operation.
If you need to add another user, invite them through the admin panel instead of reopening public registration.
Protect the Admin Panel
The admin panel is available at:
https://vault.home.arpa/admin
For this local deployment, access is already limited to devices that can reach your network.
If Vaultwarden is later exposed to the internet, add another layer of protection around /admin, such as a VPN, reverse-proxy access rule, or identity-aware access policy.
Keep the admin password separate from your personal vault master password.
Keep Vaultwarden Updated
This guide pins Vaultwarden to a specific version for reproducibility.
Before upgrading:
- Take a backup.
- Review the release notes.
- Update the image tag in
/opt/vaultwarden/compose.yaml.
Then run:
cd /opt/vaultwarden
docker compose pull
docker compose up -d
docker logs --tail 100 vaultwarden
Confirm that the container starts normally and that you can still log in and synchronize your vault.
Keep the Vaultwarden Backend Private
The Compose file intentionally does not publish Vaultwarden directly to the RK1 with a mapping such as:
ports:
- "8000:80"
Caddy reaches Vaultwarden internally over the shared proxynet Docker network:
Caddy → vaultwarden:80
This keeps Vaultwarden’s plain HTTP backend off the host network and ensures normal access goes through Caddy over HTTPS.
Part 9: Back Up and Restore Vaultwarden
Vaultwarden stores its SQLite database separately from attachments, Sends, server keys, and other files in the data directory. A Vaultwarden application-data backup should preserve both.
This deployment stores Vaultwarden data in:
/mnt/nvme/vaultwarden/data
and backups in:
/mnt/nvme/backups/vaultwarden
Vaultwarden includes a built-in backup command that creates a consistent SQLite backup while the service is running. The rest of the data directory should be archived separately. Because the database snapshot and file archive are created sequentially rather than as one atomic snapshot, schedule the backup for a quiet period when changes are unlikely.
Create the Backup Script
Create:
nano /opt/vaultwarden/backup-vaultwarden.sh
Add:
#!/usr/bin/env bash
set -euo pipefail
DATA_DIR="/mnt/nvme/vaultwarden/data"
BACKUP_DIR="/mnt/nvme/backups/vaultwarden"
STAMP="$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"
docker exec vaultwarden /vaultwarden backup
LATEST_DB="$(
find "$DATA_DIR" \
-maxdepth 1 \
-type f \
-name 'db_*.sqlite3' \
-printf '%T@ %p\n' \
| sort -nr \
| head -n 1 \
| cut -d' ' -f2-
)"
if [[ -z "${LATEST_DB:-}" || ! -f "$LATEST_DB" ]]; then
echo "Vaultwarden database backup was not created" >&2
exit 1
fi
cp "$LATEST_DB" "$BACKUP_DIR/db-$STAMP.sqlite3"
rm -f "$LATEST_DB"
tar \
--exclude='db.sqlite3' \
--exclude='db.sqlite3-shm' \
--exclude='db.sqlite3-wal' \
--exclude='db_*.sqlite3' \
-czf "$BACKUP_DIR/files-$STAMP.tar.gz" \
-C "$DATA_DIR" .
find "$BACKUP_DIR" \
-maxdepth 1 \
-type f \
-mtime +14 \
-delete
echo "Backup completed:"
echo "$BACKUP_DIR/db-$STAMP.sqlite3"
echo "$BACKUP_DIR/files-$STAMP.tar.gz"
Make the script executable:
chmod 700 /opt/vaultwarden/backup-vaultwarden.sh
Run the first backup manually:
/opt/vaultwarden/backup-vaultwarden.sh
A successful run should report that the SQLite backup was created and show the two backup files.
Inspect the backup directory:
ls -lh /mnt/nvme/backups/vaultwarden
You should see a pair of files similar to:
db-20260807-204444.sqlite3
files-20260807-204444.tar.gz
The database file contains the consistent SQLite backup, while the archive stores the remaining Vaultwarden data such as server keys, attachments, Sends, configuration files, and other files present in /data.
Schedule Daily Backups
Open the current user’s crontab:
crontab -e
If prompted to select an editor, choose Nano.
Add:
15 3 * * * /opt/vaultwarden/backup-vaultwarden.sh >> /mnt/nvme/backups/vaultwarden/backup.log 2>&1
This runs the backup every day at 03:15.
Verify that the cron entry was saved:
crontab -l
You should see:
15 3 * * * /opt/vaultwarden/backup-vaultwarden.sh >> /mnt/nvme/backups/vaultwarden/backup.log 2>&1
The script deletes local backup files older than 14 days.
A backup stored on the same NVMe is not enough by itself. If the drive fails, both Vaultwarden and its backups could be lost. Copy the backup directory periodically to another computer, NAS, or encrypted remote storage.
Verify the Backup Contents
Before relying on the backup, confirm that it can be reconstructed into a complete Vaultwarden data directory.
Create a temporary restore directory inside the backup path:
RESTORE_DIR=/mnt/nvme/backups/vaultwarden/restore-test
BACKUP_DIR=/mnt/nvme/backups/vaultwarden
rm -rf "$RESTORE_DIR"
mkdir -p "$RESTORE_DIR"
LATEST_FILES="$(find "$BACKUP_DIR" -maxdepth 1 -type f -name 'files-*.tar.gz' | sort | tail -n 1)"
LATEST_DB="$(find "$BACKUP_DIR" -maxdepth 1 -type f -name 'db-*.sqlite3' | sort | tail -n 1)"
tar -xzf "$LATEST_FILES" -C "$RESTORE_DIR"
cp "$LATEST_DB" "$RESTORE_DIR/db.sqlite3"
find "$RESTORE_DIR" -maxdepth 2 -type f | sort
The output should include:
db.sqlite3
along with other Vaultwarden files present in your deployment, such as server keys, attachments, cached icons, or configuration files.
This is a reconstruction test only. It does not modify the running Vaultwarden installation.
Restore Vaultwarden
If the real installation ever needs to be restored:
- Stop Vaultwarden.
- Move the existing data directory somewhere safe instead of deleting it.
- Extract the latest matching
files-*.tar.gzarchive into the Vaultwarden data directory. - Copy the matching database backup into the directory as
db.sqlite3. - Make sure stale
db.sqlite3-walanddb.sqlite3-shmfiles are not present. - Start Vaultwarden again.
- Log in and verify several vault items and any attachments.
Stop Vaultwarden with:
cd /opt/vaultwarden
docker compose down
After restoring the files, start it again:
docker compose up -d
Check the logs:
docker logs --tail 100 vaultwarden
A normal startup should end with Vaultwarden launching successfully on its internal HTTP port.
Never overwrite or delete the only copy of the current Vaultwarden data while testing or performing a restore.
Troubleshooting
The web vault says HTTPS is required
Make sure you are opening the configured HTTPS hostname:
https://vault.home.arpa
Do not use the container IP or an http:// URL.
Check the configured Vaultwarden domain:
docker exec vaultwarden printenv DOMAIN
Expected output:
https://vault.home.arpa
If it differs, update /opt/vaultwarden/.env and recreate the container.
Check Caddy if HTTPS is still not working:
docker logs --tail 100 caddy
If the page loads but the browser shows a certificate warning, make sure Caddy’s root CA certificate is installed and trusted on that device.
Caddy returns 502 Bad Gateway
Confirm that both Caddy and Vaultwarden are running:
docker ps --format 'table {{.Names}}\t{{.Status}}'
Both containers should show as running.
Confirm they are connected to the shared proxynet network:
docker network inspect proxynet \
--format '{{range $id, $container := .Containers}}{{$container.Name}}{{"\n"}}{{end}}'
You should see:
caddy
vaultwarden
Test whether Caddy can resolve the Vaultwarden container:
docker exec caddy getent hosts vaultwarden
If this does not return an address, check the Docker network configuration in both Compose files.
The admin page rejects the password
Confirm that the hashed admin token reached the container:
docker exec vaultwarden printenv ADMIN_TOKEN
It should begin with:
$argon2id$
The running value should not contain surrounding quote characters or doubled dollar signs.
Validate the Compose configuration:
cd /opt/vaultwarden
docker compose config --quiet
If you previously changed settings through the Vaultwarden admin panel, also check:
/mnt/nvme/vaultwarden/data/config.json
Settings saved through the admin interface can override equivalent environment variables.
Public registration is still available
Check the value used by the running container:
docker exec vaultwarden printenv SIGNUPS_ALLOWED
Expected output:
false
If it still shows true, confirm that /opt/vaultwarden/compose.yaml contains:
SIGNUPS_ALLOWED: "false"
Then recreate the container:
cd /opt/vaultwarden
docker compose up -d --force-recreate
Open the web vault again in a private browser window and confirm that account creation is no longer available.
Browser sync works but changes are not instant
Vaultwarden uses WebSockets for real-time updates between clients.
In the browser developer tools, open the Network tab, filter for WS, and look for:
/notifications/hub
A working connection should return:
101 Switching Protocols
Caddy handles WebSocket proxying automatically for this deployment.
Do not add an old Vaultwarden configuration that proxies WebSockets through port 3012. Current Vaultwarden releases use the main HTTP endpoint.
Manual synchronization can still work even if the real-time WebSocket connection is unavailable.
The Bitwarden mobile app cannot connect
First confirm that the app is configured as Self-hosted with:
https://vault.home.arpa
as the Server URL.
For this deployment, leave the separate Web Vault, API, Identity, Icons, and mTLS fields unchanged.
If the app reports that it cannot verify the server certificate, the phone probably does not trust Caddy’s internal certificate authority.
Export the Caddy root certificate on the RK1:
docker exec caddy \
cat /data/caddy/pki/authorities/local/root.crt \
> ~/caddy-root.crt
Transfer caddy-root.crt to the phone and install it as a trusted CA certificate.
Then fully close and reopen the Bitwarden app and try the self-hosted login again.
Also confirm that:
- The phone can resolve
vault.home.arpa - The phone is connected to the local network or another network that can reach the RK1
- The account was created on this Vaultwarden server rather than only on Bitwarden Cloud
The same email address can exist independently on Bitwarden Cloud and on your Vaultwarden instance.
vault.home.arpa does not resolve
From a client using Pi-hole for DNS, run:
dig vault.home.arpa
Expected result:
192.168.x.x
Replace that address with your RK1 IP.
If the hostname does not resolve, confirm that your local DNS server contains a record mapping:
vault.home.arpa → 192.168.x.x
Also confirm that the client is actually using Pi-hole as its DNS server.
The browser shows a certificate warning
This deployment uses:
tls internal
so Caddy issues the certificate from its own private certificate authority.
The device must trust Caddy’s root certificate before https://vault.home.arpa will appear as fully trusted.
Export the root certificate with:
docker exec caddy \
cat /data/caddy/pki/authorities/local/root.crt \
> ~/caddy-root.crt
Install that certificate as a trusted CA on each browser, desktop, or mobile device that will access Vaultwarden.
Do not rely on manually bypassing the browser certificate warning for normal use.
Data disappears after recreating the container
Confirm the Vaultwarden volume mapping:
docker inspect vaultwarden \
--format '{{range .Mounts}}{{println .Source "->" .Destination}}{{end}}'
Expected mapping:
/mnt/nvme/vaultwarden/data -> /data
If /data is not mapped to persistent host storage, stop using the instance until the volume configuration is corrected.
The backup script fails
Run the backup manually:
/opt/vaultwarden/backup-vaultwarden.sh
Then check:
ls -lh /mnt/nvme/backups/vaultwarden
A successful backup should create matching files similar to:
db-20260807-204444.sqlite3
files-20260807-204444.tar.gz
If the script reports permission errors, confirm that the current user owns the Vaultwarden backup directory:
ls -ld /mnt/nvme/backups/vaultwarden
The backup script must be able to create files in that directory.
The scheduled backup is not running
Confirm that the cron entry was saved:
crontab -l
You should see:
15 3 * * * /opt/vaultwarden/backup-vaultwarden.sh >> /mnt/nvme/backups/vaultwarden/backup.log 2>&1
After the scheduled time has passed, inspect:
cat /mnt/nvme/backups/vaultwarden/backup.log
and:
ls -lh /mnt/nvme/backups/vaultwarden
to confirm that a new backup was created.
What You’ve Built
At this point, Vaultwarden is running as a complete self-hosted password manager on the Turing Pi RK1 rather than just another container in the homelab.
The service is stored persistently on NVMe, exposed through Caddy over HTTPS, and accessible from the official Bitwarden browser, mobile, and desktop clients. Public registration has been disabled, the admin interface is protected with a hashed token, two-step login can be enabled for the vault itself, and real-time synchronization works through the same HTTPS endpoint.
The deployment also has a recovery path. Vaultwarden’s SQLite database and the rest of its data directory are backed up separately, daily backups can be automated with cron, and the backup set can be reconstructed into a usable Vaultwarden data directory if the live installation ever needs to be restored.
That recovery path matters more here than it would for most homelab services. If a dashboard or media server goes down, it is mostly an inconvenience. If a password manager goes down without a working backup, the consequences can be much more serious.
For that reason, the ongoing maintenance is simple but important: keep Vaultwarden reasonably up to date, keep at least one backup somewhere other than the RK1 itself, protect the two-step login recovery code, and verify occasionally that the backups you are creating are actually usable.
Once those habits are in place, the RK1 can run Vaultwarden as a lightweight, private password-management service without dedicating an entire node to it.
Related Articles
- Turing Pi 2.5 + RK1 Complete Setup Guide: Set up the board, flash Ubuntu, configure networking, and bring the RK1 online.
- Self-Hosted Apps on Turing Pi 2.5: See where Vaultwarden fits within a complete ARM64 homelab stack.
- Expose Self-Hosted Services with Cloudflare Tunnel: Publish Vaultwarden without forwarding inbound web ports.
- Self-Hosted WireGuard VPN on Turing Pi: Keep Vaultwarden private and reach it remotely through a VPN.
- Gitea on Turing Pi 2.5: Deploy another lightweight, NVMe-backed service using Docker Compose.
FAQ
Is Vaultwarden the official Bitwarden server?
No. Vaultwarden is an unofficial, community-maintained implementation of the Bitwarden client API. It works with the official Bitwarden browser extension, mobile apps, desktop clients, and CLI, but it is not affiliated with or supported by Bitwarden, Inc.
Does Vaultwarden work on the RK1’s ARM64 processor?
Yes. The standard vaultwarden/server image supports Linux ARM64 and runs normally on the RK3588 used by the Turing Pi RK1.
Does Vaultwarden need a 16GB or 32GB RK1?
No. Vaultwarden is lightweight enough for an 8GB RK1. Higher-memory modules mainly give you more headroom to run other services on the same node.
Can I keep Vaultwarden accessible only on my local network?
Yes. That is exactly what the vault.home.arpa setup in this guide does. Clients connect through the private hostname over HTTPS, and devices must be able to resolve that hostname and trust Caddy’s internal certificate authority.
Can I access this Vaultwarden instance remotely later?
Yes. You can keep the service private and reach your home network through WireGuard or Tailscale, or expose Vaultwarden through something such as Cloudflare Tunnel. If you make it internet-accessible, review the admin-panel and access-control configuration before doing so.
Do I need PostgreSQL or MySQL?
No. SQLite is sufficient for a normal personal, family, or small-team deployment and keeps the stack much simpler. This guide uses SQLite throughout.
Why are signups enabled during installation?
They are enabled temporarily so the first Vaultwarden account can be created. After that, the guide changes:
SIGNUPS_ALLOWED: "true"
to:
SIGNUPS_ALLOWED: "false"
Additional users can then be added intentionally instead of leaving public registration open.
Are my passwords stored as plaintext in db.sqlite3?
No. Vault data is encrypted by the Bitwarden client before it is stored by Vaultwarden. The database and backup files are still sensitive, however, and should be protected along with the rest of the Vaultwarden data directory.
Why does the Bitwarden mobile app say it cannot verify the server certificate?
This happens when the device does not trust Caddy’s internal certificate authority. Export Caddy’s root certificate, install it as a trusted CA certificate on the phone, then reopen Bitwarden and connect again to:
https://vault.home.arpa
What happens if the RK1 goes offline?
Clients generally retain their most recently synchronized encrypted vault locally, so previously synced items can still be available. New changes cannot synchronize until the Vaultwarden server becomes reachable again.
Can I use the same email address on Bitwarden Cloud and Vaultwarden?
Yes. They are separate server environments. Make sure the Bitwarden client is set to Self-hosted and pointed at:
https://vault.home.arpa
before logging in.
How should I update Vaultwarden?
Take a backup first, review the release notes, update the pinned image tag in /opt/vaultwarden/compose.yaml, then run:
cd /opt/vaultwarden
docker compose pull
docker compose up -d
docker logs --tail 100 vaultwarden
After the update, confirm that the web vault loads and that at least one connected client can log in and synchronize normally.