Secrets management
By the time you’ve wired up a dozen apps, secrets are everywhere: database passwords in compose files, API keys in .env, OIDC client secrets duplicated between each app and the IdP. A secrets manager gives you one encrypted source of truth, with versioning and rotation, and injects values at runtime instead of committing them to disk.
This lab uses Infisical (self-hostable, free tier covers the essentials). Vaultwarden covers human credentials; a secrets manager covers machine ones — different jobs.
The shape
Section titled “The shape”- A secrets manager container holds all values, encrypted, organized per app.
- A machine identity (client ID + secret) lets automation read them.
- A small render step pulls each app’s secrets and writes them to the file the app already reads (its
.env), then the app is (re)created. - A timer re-renders periodically so rotations propagate; the last rendered file stays on disk as an outage fallback.
secrets manager ──(machine identity)──▶ render script ──▶ /opt/app/.env ──▶ container ▲ ▲ │ organized per-app folders │ timer: re-render + keep last as fallbackInjecting into compose
Section titled “Injecting into compose”Keep the plaintext out of compose.yaml by referencing variables, and render the .env the compose interpolates from:
services: app: environment: DB_PASSWORD: ${DB_PASSWORD} # value comes from the rendered .env OIDC_CLIENT_SECRET: ${OIDC_CLIENT_SECRET}# render step (per app), run by a systemd timerinfisical export --path=/app --format=dotenv > /opt/app/.envdocker compose up -d app # only recreates if the resolved config changedNow compose.yaml is clean enough to commit or hand to a fleet manager; the secret only lands in a root-readable, runtime-generated .env.
Hard-won details
Section titled “Hard-won details”Where to start
Section titled “Where to start”Migrate the highest-leverage secrets first:
- OIDC client secrets — you’ve duplicated these across every app and the IdP.
- The DNS/ACME API token — a single, high-value credential sitting in your proxy config.
- Database passwords and app signing keys.
Do it app-by-app, verify each still starts, and keep the old .env as a backup until you’re sure.