8 Secrets Management
Note
Bitwarden as the source of truth, podman secret as the runtime interface, Vaultwarden as a self-hosted backup.
8.1 The secret lifecycle
- Secrets are created and stored in Bitwarden (cloud-hosted vault)
- At deploy time, pyinfra retrieves secrets via the Bitwarden CLI and injects them as podman secrets
- At runtime, containers read secrets from files mounted by podman (not environment variables)
- Secrets never appear in quadlet files, environment blocks, or the Git repo
8.2 Bitwarden item conventions
- One Bitwarden item per service, named to match the service name in
pyinfra/services.py - Custom fields map to individual secrets (e.g.,
AUTHENTIK_SECRET_KEY,PG_PASS) - pyinfra looks up items by name, extracts fields, creates
podman secretentries for the service user
8.3 podman secret integration
- Quadlet
.containerfiles reference secrets by name:Secret=secret_name,type=mount - Podman mounts the secret as a file inside the container (default:
/run/secrets/{name}) - Service users each have their own secret namespace; secrets are not shared across users
8.4 Vaultwarden as break-glass
- Vaultwarden runs on the host as a self-hosted Bitwarden-compatible server
- If Bitwarden cloud is unreachable, Vaultwarden has a local copy of all credentials
- Family members use Vaultwarden for their own password management; the maintainer uses it as a fallback for infrastructure secrets
8.5 Why not Vault, SOPS, or dotenv
- HashiCorp Vault: powerful but a full distributed system to maintain; overkill for a single host
- SOPS: good for encrypting files in Git, but secrets still need to reach
podman secretat runtime — adds a layer without removing one .envfiles: secrets in plaintext on disk, no audit trail, easy to accidentally commitpodman secretwith Bitwarden as the source of truth is the simplest path that keeps secrets out of the repo and off disk in plaintext