If you've ever typed a password into a browser and thought "this really shouldn't live on someone else's server," Vaultwarden is worth an evening of your time. It's a self-hosted password vault that speaks the same protocol as Bitwarden, so every official Bitwarden app — browser extension, phone app, desktop client, even the CLI — just works against it. No subscription, no third party holding your vault, and it's light enough to run comfortably in a Proxmox LXC container next to whatever else you've already got going.
This guide builds Vaultwarden from source inside a plain Debian container. No Docker, no prebuilt appliance, no helper script doing things behind your back. You'll see exactly what gets installed and why, which matters a lot more for something holding your passwords than it does for, say, a media server.
What You Will Learn
By the end of this walkthrough you'll have a working Vaultwarden instance running in its own LXC container, reachable from your browser and ready to connect to the real Bitwarden apps. Specifically, you'll learn how to:
- Create a right-sized LXC container for Vaultwarden
- Compile Vaultwarden from source using Rust's cargo build system
- Install the official web vault interface alongside it
- Run it as a proper systemd service instead of a screen session that dies on reboot
- Lock down the admin panel and fix the handful of errors almost everyone hits on their first attempt
What Is This Feature?
Vaultwarden is an unofficial, open-source server implementation of the Bitwarden API, written in Rust. It started life under the name bitwarden_rs, built by a developer who wanted the official Bitwarden server without its heavier resource requirements. The project was later renamed, but the goal hasn't changed: give you a password vault that talks to real Bitwarden clients without needing to run Bitwarden's own .NET-based server, which historically wanted several containers and a fair chunk of RAM just to idle.
If you haven't come across LXC before, it stands for Linux Containers, and it's Proxmox VE's lightweight alternative to a full virtual machine. Instead of emulating an entire PC — CPU, BIOS, disk controller, the works — an LXC container shares the host's Linux kernel and only isolates the processes and filesystem sitting on top of it. That's why containers start in under a second and can run comfortably on a fraction of the RAM a VM would need. For something like Vaultwarden, which idles at well under 100 MB of memory, a container is a much better fit than a full VM.
Why Would You Use It?
Password manager subscriptions aren't expensive individually, but they add up, and you're trusting a company you don't control with the keys to every account you own. Self-hosting flips that. Your vault lives on hardware sitting in your house, backed up on your schedule, accessible only through paths you set up yourself.
Vaultwarden specifically earns its popularity because it's compatible with the clients people already use. Nobody has to install some unfamiliar app — your family or your coworkers can keep using the Bitwarden browser extension and mobile app they already know, just pointed at your server instead of Bitwarden's cloud. And because it's written in Rust rather than .NET, it runs happily on hardware that would make the official server sweat: an old mini PC, a Raspberry Pi-class board, or in this case, a single LXC container tucked away on a Proxmox host that's also running six other things.
I'll be honest — for a single person, the effort-to-benefit ratio only really pays off once you're already running a homelab for other reasons. But if you are, this is one of the more satisfying services to self-host, because you actually feel the difference every time you log into something.
Prerequisites
Before you start, make sure you have:
- A working Proxmox VE 8.x or 9.x host with internet access
- A Debian 13 (Trixie) or Debian 12 (Bookworm) container template downloaded — grab it from local (Templates) in the Proxmox web UI if you don't have one yet
- At least 2 GB of RAM temporarily available for the container (compiling Rust code is memory-hungry; you can scale it back down to 512 MB once the build is done)
- About 20 minutes, most of which is the compiler doing its thing while you get coffee
- Basic comfort typing commands into a terminal — nothing exotic, but you should know how to use a text editor like nano
You don't need a domain name to follow this guide, but you will need one eventually if you want the real Bitwarden apps to sync over the internet, since they refuse to talk to a vault over plain HTTP except on localhost.
Step-by-Step Tutorial
Step 1: Create the LXC Container
In the Proxmox web UI, click Create CT in the top right. Walk through the wizard with these settings:
- Hostname: vaultwarden
- Template: debian-13-standard (or debian-12-standard if that's what you have)
- Disk: 6 GB is plenty — the compiled binary and web vault together are under 200 MB
- CPU: 2 cores (helps the build finish faster; you can drop this to 1 afterward)
- Memory: 2048 MB during setup
- Network: vmbr0, DHCP is fine to start, though a static IP or DHCP reservation makes life easier once you're pointing devices at this thing permanently
Leave the container unprivileged unless you have a specific reason not to — there's no part of this setup that needs privileged access to the host.
If you'd rather do this from the shell, the equivalent looks like this (adjust the storage name and VMID to match your setup):
pct create 210 local:vzdump/debian-13-standard_13.1-1_amd64.tar.zst \
--hostname vaultwarden \
--cores 2 \
--memory 2048 \
--rootfs local-lvm:6 \
--net0 name=eth0,bridge=vmbr0,ip=dhcp \
--unprivileged 1 \
--features nesting=1
pct start 210
Step 2: Update the Container and Install Build Dependencies
Open a console to the container (either the Console tab in the web UI, or pct enter 210 from the Proxmox shell) and update it first:
apt update && apt full-upgrade -y
Then install the packages the Rust build needs. This is the part people skip and then get confused by cryptic linker errors ten minutes into a compile:
apt install -y build-essential pkg-config libssl-dev sqlite3 libsqlite3-dev git curl
build-essential gives you a C compiler and linker, which Rust's build system calls out to. libssl-dev and pkg-config let Vaultwarden link against OpenSSL. libsqlite3-dev is needed because we're using SQLite as the database — the simplest option for a single-instance setup like this one.
Step 3: Create a Dedicated System User
Don't run Vaultwarden as root. It's holding password data; give it its own account with no login shell:
useradd --system --create-home --home-dir /opt/vaultwarden --shell /usr/sbin/nologin vaultwarden
Step 4: Install Rust and Compile Vaultwarden
Vaultwarden doesn't ship prebuilt binaries for bare-metal installs — its official distribution channel is Docker, and everything else gets compiled from source. That sounds intimidating, but it's really just a few commands.
Install Rust using the official rustup installer, run as your normal shell user (not the vaultwarden system account):
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source "$HOME/.cargo/env"
Now clone the source and build it:
git clone https://github.com/dani-garcia/vaultwarden.git
cd vaultwarden
cargo build --release --features sqlite
Go do something else for ten to fifteen minutes. On a couple of vCPUs this compiles the whole dependency tree from scratch, and it will pin the CPU the entire time. When it's done, you'll have a binary at target/release/vaultwarden.
Step 5: Install the Web Vault
The web vault — the actual browser UI you log into — is a separate project (bw_web_builds) because building it needs a whole Node.js toolchain that has nothing to do with the Rust server. Vaultwarden's maintainers publish prebuilt web vault bundles precisely so you don't have to deal with that.
Check which web vault version your checkout expects:
cat web-vault-version
Then grab the matching release from the dani-garcia/bw_web_builds releases page on GitHub and extract it:
mkdir -p /opt/vaultwarden/web-vault
curl -LO https://github.com/dani-garcia/bw_web_builds/releases/download/vX.X.X/bw_web_vX.X.X.tar.gz
tar -xzf bw_web_v*.tar.gz -C /opt/vaultwarden/web-vault --strip-components=1
Replace vX.X.X with the version number the previous command showed you — this is the one place where you need to fill in a real value yourself, since it changes with every Vaultwarden release.
Step 6: Move the Binary and Set Permissions
cp target/release/vaultwarden /opt/vaultwarden/vaultwarden
mkdir -p /var/lib/vaultwarden
chown -R vaultwarden:vaultwarden /opt/vaultwarden /var/lib/vaultwarden
Step 7: Generate an Admin Token and Write the Config
The admin panel lets you manage users and see diagnostics from a browser, so it needs its own password — not your vault master password. Generate a properly hashed token:
sudo -u vaultwarden /opt/vaultwarden/vaultwarden hash
It'll prompt you for a passphrase and print out a long Argon2id string starting with $argon2id$. Copy the whole thing — that's your ADMIN_TOKEN.
Create /opt/vaultwarden/vaultwarden.env:
DATA_FOLDER=/var/lib/vaultwarden
WEB_VAULT_FOLDER=/opt/vaultwarden/web-vault
ROCKET_ADDRESS=0.0.0.0
ROCKET_PORT=8080
DOMAIN=http://vaultwarden.local:8080
ADMIN_TOKEN=$argon2id$paste-your-full-hash-here
SIGNUPS_ALLOWED=true
WEBSOCKET_ENABLED=true
Set DOMAIN to whatever address you'll actually reach this container at — the IP address is fine for now. You'll want to change it once you put a real hostname and HTTPS in front of it.
Step 8: Create the systemd Service
Create /etc/systemd/system/vaultwarden.service:
[Unit]
Description=Vaultwarden Password Manager
After=network.target
[Service]
User=vaultwarden
Group=vaultwarden
EnvironmentFile=/opt/vaultwarden/vaultwarden.env
ExecStart=/opt/vaultwarden/vaultwarden
WorkingDirectory=/opt/vaultwarden
Restart=on-failure
[Install]
WantedBy=multi-user.target
Then enable and start it:
systemctl daemon-reload
systemctl enable --now vaultwarden
Step 9: Check That It's Actually Running
systemctl status vaultwarden
curl -I http://127.0.0.1:8080
You're looking for active (running) in the status output and an HTTP/1.1 200 OK from curl. If both check out, open http://<container-ip>:8080 in a browser on your network and you should see the Vaultwarden login screen. Create your account on that first screen — this is the one time you'll use the signup form, since you'll want to turn it off right after.
Commands Explained
| Command | What It Does |
|---|---|
pct enter 210 | Opens an interactive shell directly inside container 210 from the Proxmox host, without needing SSH |
useradd --system --shell /usr/sbin/nologin | Creates a service account that can own files but can never be used to log in interactively |
cargo build --release --features sqlite | Compiles Vaultwarden with optimizations enabled and SQLite support compiled in |
vaultwarden hash | A built-in subcommand that turns a passphrase you type into a secure Argon2id hash, suitable for the admin token |
systemctl enable --now vaultwarden | Enables the service to start on every boot and starts it immediately in one step |
journalctl -u vaultwarden -f | Streams the live log output for the Vaultwarden service, useful for catching errors as they happen |
Common Errors
A few things trip up almost everyone the first time through:
error: linker `cc` not found. You skipped build-essential in Step 2. Install it and rerun cargo build — it'll pick up where it left off rather than starting over.
failed to run custom build command for `openssl-sys`. Missing libssl-dev and pkg-config. Same fix: install them, rerun the build.
The web vault loads a blank white page. Almost always means WEB_VAULT_FOLDER in your env file doesn't point at a folder that actually contains index.html. Double-check the path from Step 5 and confirm the extraction actually put files there rather than into a nested subdirectory.
"Playing it safe and canceling connection" in the browser extension. Bitwarden clients refuse to sync over plain HTTP unless you're connecting to localhost. You'll need HTTPS in front of this before the extension or mobile app will talk to it — more on that in Best Practices below.
vaultwarden.service: Main process exited, code=exited, status=1. Check journalctl -u vaultwarden -e for the real reason, but nine times out of ten it's a permissions problem — /var/lib/vaultwarden owned by root instead of the vaultwarden user, usually because a command in Step 6 got skipped or run as the wrong user.
Troubleshooting
When something's not working, this is the order I'd check things in:
- Run
systemctl status vaultwardenfirst. If it says the service is inactive or failed, everything else you check is downstream of that. - Pull the actual error with
journalctl -u vaultwarden -e --no-pager. The service log almost always names the exact file or permission it's unhappy about. - Confirm ownership with
ls -la /var/lib/vaultwarden /opt/vaultwarden. Everything should belong to the vaultwarden user, not root. - Test locally before blaming the network:
curl -I http://127.0.0.1:8080from inside the container. If that fails, the problem is Vaultwarden itself, not your firewall or reverse proxy. - If local curl works but you can't reach it from another machine, check that the container's firewall (if you enabled one) allows port 8080, and that you're using the container's actual IP, not 127.0.0.1.
Best Practices
Turn off open signups the moment you've created the accounts you need. Edit vaultwarden.env, set SIGNUPS_ALLOWED=false, and restart the service with systemctl restart vaultwarden. Leaving signups open on an internet-facing instance is asking for trouble.
Put a real reverse proxy with a valid TLS certificate in front of this before you rely on it from outside your LAN. If you've already got Nginx Proxy Manager or Caddy running elsewhere on your network, point a subdomain at this container's IP and let it handle the certificate — Vaultwarden itself doesn't need to know or care that it's sitting behind one.
Back up /var/lib/vaultwarden regularly. It's a single SQLite file plus an attachments folder, so it's small and easy to back up on its own, but I'd still lean on your normal Proxmox VE backup job (vzdump) for the whole container as a second layer — belt and suspenders for something this sensitive.
Keep the source updated. Vaultwarden is a security-relevant service, and new releases regularly include fixes worth having. A quick git pull && cargo build --release --features sqlite followed by a service restart is all it takes.
Once the build finishes, dial the container back down to 1 vCPU and 512 MB of RAM. Vaultwarden itself is genuinely light — the 2 GB was only ever for the compiler.
Frequently Asked Questions
Do I need Docker to run Vaultwarden?
No. Docker is the officially supported path, but building from source with cargo, like this guide does, works just as well and gives you a plain systemd service instead of a container inside a container.
Is Vaultwarden safe to trust with real passwords?
It's been running in production for thousands of self-hosters for years and uses the same encryption model as official Bitwarden clients — your vault is encrypted client-side before it ever reaches the server. The bigger risk is usually your own setup: skipping HTTPS or leaving the admin panel wide open matters more than the software itself.
Can I use the real Bitwarden mobile app with this?
Yes. In the app's settings, there's a "self-hosted" option where you enter your server's URL before logging in. Everything else behaves exactly like the official service.
How much storage does it actually need?
Very little. The database is SQLite, and unless you're storing large file attachments, a few hundred megabytes will last you years. The 6 GB disk in this guide has a lot of headroom.
Do I need a domain name to get started?
Not to follow this guide and log in from your own network. You'll want one once you're ready to connect over HTTPS from outside the LAN, since the browser extension and mobile app both require a valid certificate for anything other than localhost.
What happens to my vault if the Proxmox host goes down?
Nothing happens to the data itself — it's sitting on disk in the container same as any other file. But you won't be able to reach it until the host is back up, which is worth remembering if you're planning to rely on this vault for accounts you need during an outage.
Conclusion
You now have a password vault that answers only to you, running on hardware in your own house, built from source so nothing about it is a mystery. It took about twenty minutes, most of it spent waiting on a compiler. From here, the two things actually worth doing next are locking down that admin token and getting a real certificate in front of it — after that, it's just another service quietly doing its job in the background, which is exactly what you want from something holding your passwords.