Skip to content

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.

  1. A secrets manager container holds all values, encrypted, organized per app.
  2. A machine identity (client ID + secret) lets automation read them.
  3. 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.
  4. 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 fallback

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}
Terminal window
# render step (per app), run by a systemd timer
infisical export --path=/app --format=dotenv > /opt/app/.env
docker compose up -d app # only recreates if the resolved config changed

Now compose.yaml is clean enough to commit or hand to a fleet manager; the secret only lands in a root-readable, runtime-generated .env.

Migrate the highest-leverage secrets first:

  1. OIDC client secrets — you’ve duplicated these across every app and the IdP.
  2. The DNS/ACME API token — a single, high-value credential sitting in your proxy config.
  3. 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.

Next: the part people skip until it’s too late →