Passwords an AI Agent Can Actually Use: Self-Hosting Vaultwarden
The problem this actually solves
Most people reach for a password manager because typing the same weak password everywhere is a bad idea — and that’s true, but it wasn’t my main motivation. My phone’s built-in password manager (Google Password Manager, in my case, though the same is true of Samsung Pass or Apple Keychain) has no CLI and no API a script or an AI agent can call. If one of my AI agents needs a credential, the only way to get it out of a consumer password manager like that is a human manually copying it and pasting it somewhere — which means the secret briefly exists in plaintext outside the vault, every single time. Once I started building AI agents that needed to authenticate to things on their own, that became untenable.
What it does
Vaultwarden is the free, self-hosted, Rust-based reimplementation of the Bitwarden server. It speaks the same protocol as the official Bitwarden apps — browser extension, mobile, desktop, and critically, the bw command-line client — but the vault lives on my own hardware instead of Bitwarden’s cloud. Beyond logins, it stores secure notes, cards, and identities in the same reachable vault, so it’s doubled as general secure storage for anything an agent needs, not just website passwords.
The part that actually mattered: scriptable access
The setup itself is a single Docker container (vaultwarden/server), locked down after the first account is created (SIGNUPS_ALLOWED=false, plus a long random admin token gating the /admin panel). But the piece that made this worth building was the CLI path: bw login once interactively, then bw unlock returns a session key. Export that as BW_SESSION, and from then on bw get password "Item Name" returns a credential directly to a script or an agent — no manual copy-paste, no human relay step, no plaintext detour.
A real compatibility bug, and the fix
Running the Vaultwarden domain through Cloudflare’s proxy (see the Cloudflare Tunnel post) introduced a genuinely annoying issue: the bw CLI can choke on Cloudflare’s zstd response compression — a documented class of problem, not unique to my setup. Browser and mobile clients hitting the public URL directly are unaffected; it’s specifically a CLI/scripting problem. The fix was running a local reverse proxy (nginx with a self-signed cert) straight to the container’s LAN port, and pointing bw config server at that local HTTPS URL instead of the public Cloudflare-proxied one. It’s the kind of bug that only shows up once you’re actually automating against something, not just clicking through a UI — exactly the layer most tutorials don’t cover.
The security tradeoff I had to actually decide on, not dodge
Full automation runs into a real tension: bw unlock needs the master password, so an agent that’s supposed to run unattended needs that one secret stored somewhere to re-unlock after its session key expires. There’s no way to make that secret disappear entirely — the goal became minimizing how many places it lives, not pretending the problem away. I landed on keeping that root secret itself out of the vault it unlocks (it can’t live inside the thing it’s the key to) and backed up through an OS-level secret store rather than a plaintext file.
Other real lessons from actually running this: bw list items and bw get item always return full decrypted data — there’s no metadata-only audit mode, so any “what’s in my vault” check has to pipe straight into something like jq and discard the output immediately, never touch a scratch file. And a bulk import from another password manager can badly over-duplicate entries with no built-in de-dup — I hit an 866-item pile that turned out to be one export imported twice, four days apart.
What it demonstrates
This project is really about closing the gap between “a password manager for humans” and “a credential store a system can depend on programmatically” — recognizing that consumer tools optimize for the wrong interface once you’re building automation, and choosing infrastructure that matches the actual requirement instead of working around the limitation. The zstd compatibility bug and the unattended-unlock tradeoff are both the kind of practical, non-obvious problems that only surface once software is genuinely relied on by other software, not just demoed.
Part of the Homelab AI Blueprints series — see all nine blueprints for building and operating a self-hosted, AI-agent-run home server.
Discover more from Michael Allen
Subscribe to get the latest posts sent to your email.
