Pull secrets from Vault at runtime
HashiCorp Vault integration for Olympus
For high-security deployments, secrets in .env files are insufficient. Use HashiCorp Vault (or similar) for runtime secret retrieval.
Why
- Secrets never on disk at rest.
- Centralized rotation.
- Audit log of secret access.
- Per-service / per-instance access tokens.
Architecture
Olympus services ── short-lived Vault token ──► Vault
│
▼
Secrets: DB password, encryption key, etc.Each service authenticates to Vault, gets secrets, uses them.
Setup
# Run Vault (dev mode for testing only)
podman run -d -p 8200:8200 --name vault \
-e VAULT_DEV_ROOT_TOKEN_ID=root-token \
hashicorp/vaultFor prod, follow Vault's deployment guide.
Store secrets
export VAULT_ADDR=https://vault.your-domain.com:8200
export VAULT_TOKEN=root-token
vault kv put secret/olympus/postgres password=mysecret
vault kv put secret/olympus/kratos cookie_secret=abc123
vault kv put secret/olympus/encryption_key value=base64bytesApp reads at startup
// entrypoint.ts
import vault from "node-vault";
const v = vault({ endpoint: process.env.VAULT_ADDR, token: process.env.VAULT_TOKEN });
const pgPassword = (await v.read("secret/data/olympus/postgres")).data.data.password;
const cookieSecret = (await v.read("secret/data/olympus/kratos")).data.data.cookie_secret;
process.env.POSTGRES_PASSWORD = pgPassword;
process.env.KRATOS_SECRETS_COOKIE = cookieSecret;
// Spawn actual app
import { spawn } from "child_process";
spawn("node", ["server.js"], { env: process.env, stdio: "inherit" });Bootstrap pulls secrets, then runs app with env populated.
Or: Vault Agent
Vault Agent fetches secrets to disk:
# vault-agent.hcl
exit_after_auth = false
pid_file = "./pidfile"
auto_auth {
method "kubernetes" {
config = {
role = "olympus"
}
}
}
template {
source = "./templates/postgres.tmpl"
destination = "/secrets/postgres.env"
}# postgres.tmpl
POSTGRES_PASSWORD={{ with secret "secret/olympus/postgres" }}{{ .Data.data.password }}{{ end }}Agent maintains the file. App sources it.
Per-service auth
Each service has its own auth method:
vault auth enable approle
vault write auth/approle/role/kratos \
policies=kratos-read \
token_ttl=1h \
token_max_ttl=24hKratos gets a role_id + secret_id. Trades for a token. Token expires; refresh periodically.
Service code:
const roleId = process.env.VAULT_ROLE_ID;
const secretId = process.env.VAULT_SECRET_ID;
const auth = await v.approleLogin({ role_id: roleId, secret_id: secretId });
v.token = auth.auth.client_token;Policies
Per-role policy:
# kratos-read policy
path "secret/data/olympus/kratos/*" {
capabilities = ["read"]
}
path "secret/data/olympus/encryption_key" {
capabilities = ["read"]
}Kratos can read its secrets. Can't write. Can't read other services' secrets.
Principle of least privilege.
Rotation
When rotating a secret:
vault kv put secret/olympus/postgres password=NEWVALUEApp: how does it know?
Option A: poll
setInterval(async () => {
const current = (await v.read("secret/data/olympus/postgres")).data.data.password;
if (current !== POSTGRES_PASSWORD) {
POSTGRES_PASSWORD = current;
// Reconnect DB
}
}, 60_000);Every minute, check. Fast detection.
Option B: webhook
Vault sends webhook on update → app reloads.
Option C: restart
After rotation, restart services. Simple. Brief downtime.
For most: option C is fine.
Dynamic secrets
Vault can generate one-time credentials (DB passwords valid 24h):
vault read database/creds/olympus-readerReturns fresh DB user + password. After TTL, revoked.
Reduces blast radius further. No long-lived DB passwords.
Disaster recovery
What if Vault is down?
- Services already running: continue with cached secrets.
- New service starting: can't read secrets → fail.
Mitigation:
- Use Vault HA (3+ instances).
- Cache secrets to local file (TTL'd).
- Fall back to env vars during outage.
Sealing
Vault is "sealed" at startup. Requires unsealing via key shares (Shamir's).
For HA: auto-unseal via cloud KMS:
seal "awskms" {
region = "us-east-1"
kms_key_id = "..."
}Vault unseals automatically using AWS KMS key. Operationally simpler than manual key shares.
Audit
Vault logs all access:
2026-05-13T15:00:00Z [INFO] vault: response: client_id=olympus-kratos path=secret/data/olympus/postgresTrail of "who read what when." For compliance.
Other secret managers
Same patterns work for:
- AWS Secrets Manager.
- GCP Secret Manager.
- Azure Key Vault.
- Doppler.
Library calls differ; concepts identical.
When to use Vault
- Multiple services need shared secrets.
- Compliance demands key audit trail.
- Frequent rotation.
- Per-service / per-pod credentials.
Smaller deployments: .env file is fine.
Migration
From .env to Vault:
- Stand up Vault.
- Move secrets to Vault.
- Update services to read from Vault at startup.
- Remove from .env (encrypted backup of .env, keep nowhere accessible).
- Verify everything works.
- Decommission old practice.
Don't rush. Test.