If you look through the repositories, scripts, and Docker Compose files of most IT environments, you will find a horror show: plain text passwords in .env files, hardcoded root SSH keys in bash scripts, and API tokens pasted into unencrypted text notes. I decided early on that my lab would follow a strict enterprise rule: zero plaintext secrets anywhere on disk.
To pull this off without making automation impossible, I self-hosted an internal Vaultwarden instance (the lightweight Rust implementation of Bitwarden) and turned it into the central secret store for my entire network. Not only does it store passwords for human use, but my automation scripts, AI assistants, and deployment tools query it dynamically at runtime. Here is how I built it and how we use it as a headless network keystore.
1. The Self-Hosted Stack: Vaultwarden Behind Caddy
Vaultwarden runs in a lightweight container on my primary Docker host (docker02 at 10.1.20.18). Because modern password managers require secure HTTPS contexts to enable browser cryptography and web vaults, it sits behind our Caddy 2 reverse proxy:
# Excerpt from /home/ntadmin/stacks/vaultwarden/docker-compose.yml
services:
vaultwarden:
image: vaultwarden/server:latest
restart: unless-stopped
environment:
- WEBSOCKET_ENABLED=true
- SIGNUPS_ALLOWED=false # Closed signups for security
- INVITATIONS_ALLOWED=false
volumes:
- ./vw_data:/data
networks:
- web_net
Caddy automatically generates internal TLS certificates using our lab’s local Root CA. Every machine on the network trusts this root, so browsing to https://vault.ittechstl.local provides a seamless, green-padlock HTTPS connection with zero self-signed browser warnings.
2. Vault Organization Architecture
To keep things clean and prevent mixing business secrets with testing infrastructure, the vault is partitioned into two distinct organizations:
| Vault Organization | Scope & Purpose | Stored Secrets |
|---|---|---|
| ITTechSTL | Production MSP Business | Client credentials, vendor portals, PSA/RMM tokens, public domain certs |
| Homelab (ittechstl) | Internal Lab Infrastructure | XCP-ng/HV2 root logins, iDRAC credentials, Zabbix API tokens, Wazuh keys |
3. Using Bitwarden CLI (bw) as a Headless Keystore
Having a pretty web vault is great for humans, but a real engineering keystore needs to be accessible from command-line scripts. This is where the official Bitwarden CLI (bw on Linux, bw.exe on Windows) comes in.
First, point the CLI at your local on-prem instance:
# Point CLI to internal server
bw config server https://vault.ittechstl.local
# Login with admin master credentials
bw login [email protected]
When you unlock the vault, it issues an ephemeral session key that lives strictly in your active terminal’s memory environment. Nothing is written to the hard drive:
# In Linux Bash:
export BW_SESSION=$(bw unlock --raw)
# In Windows PowerShell:
$env:BW_SESSION = (bw.exe unlock --raw)
4. Programmatic Secret Injection in Automation
Instead of hardcoding a database password or API token into a Python daemon or maintenance script, the script fetches the secret directly out of Vaultwarden at runtime:
import os, subprocess, json
def get_secret(item_name):
# Queries the local Vaultwarden CLI via active session
cmd = ["bw", "get", "item", item_name]
res = subprocess.check_output(cmd).decode("utf-8")
data = json.loads(res)
return data["login"]["password"]
# Grab Zabbix API token dynamically:
zabbix_token = get_secret("Zabbix API token - admin")
If our central Zabbix dependency script, our AI agent Jarvis, or a deployment runbook needs a token, it asks the vault. If the vault is locked, execution cleanly halts and asks the operator to unlock it. No credentials are ever committed to Git, leaked in Docker logs, or exposed in shell history.
5. The Security Guarantee
Treating Vaultwarden as our central network keystore has fundamentally transformed how we build systems. When an AI tool like Antigravity helps me troubleshoot or configure services, I don’t give it plaintext passwords. It uses the active Bitwarden CLI session to retrieve verified credentials on-demand.
If a password needs to be rotated—say, an API secret or server password—I update it once in the vault. Every script and agent immediately uses the new credential without needing code changes or config file edits. It takes a little more discipline upfront, but it’s the only way to run a truly professional, zero-leak lab.