Prompt
How do I build a replacement for hardcoded secrets in env files?
Latest observation
A good replacement for hardcoded secrets in .env files is to move from “store secrets in the app config” to “fetch secrets at runtime from a dedicated secrets system.” The right design depends on your deployment style, but the core pattern is usually the same.
Recommended approach
1. Keep .env for non-secrets only
Use env files for:
- local dev settings
- feature flags
- non-sensitive configuration
- service URLs, log levels, etc.
Avoid storing:
- API keys
- database passwords
- JWT signing keys
- private certificates
- OAuth client secrets
2. Store secrets in a proper secrets backend
Common choices:
- Cloud-managed: AWS Secrets Manager / SSM Parameter Store, GCP Secret Manager, Azure Key Vault
- Self-hosted: HashiCorp Vault, Doppler, Infisical, 1Password Secrets Automation
- Kubernetes: use external secrets integration rather than plain Kubernetes Secrets if possible
3. Load secrets at runtime
Instead of reading them from a committed file, your app should:
- authenticate to the secrets backend
- request only the secrets it needs
- inject them into memory or process env at startup
- refresh them if your app supports rotation
Architecture options
Option A: App fetches secrets directly
Your application calls the secrets manager on startup.
Pros
- simple mental model
- no extra sidecar agent
Cons
- app needs cloud/auth SDK
- more code changes
- every app must know how to talk to secrets backend
Good when:
- you control the app code
- you want tighter integration and rotation logic
Option B: Secrets injected by a sidecar/agent
A separate process or agent fetches secrets and writes them to:
- memory
- a mounted file
- environment variables at startup
Examples:
- Vault Agent
- AWS Secrets Manager with ECS task role + init container
- Kubernetes External Secrets / CSI driver
Pros
- app code stays simpler
- easier migration from
.env - centralized rotation
Cons
- extra infrastructure
- more moving parts
Good when:
- you want to standardize across many apps
- you need policy-driven access control
A practical migration plan
Step 1: Inventory your secrets
Make a list of:
- secret name
- where it’s used
- sensitivity
- rotation requirements
- owning team/service
Step 2: Classify config vs secret
Move anything non-sensitive out of secrets storage.
Example:
DB_HOST→ configDB_PASSWORD→ secretS3_BUCKET→ configS3_ACCESS_KEY_ID/S3_SECRET_ACCESS_KEY→ secret
Step 3: Choose a secrets system
Pick based on your environment:
- AWS: Secrets Manager + IAM roles
- Kubernetes: External Secrets Operator or Vault CSI
- Multi-cloud / on-prem: Vault or Infisical
- Small team / simple ops: Doppler or 1Password automation
Step 4: Replace .env consumption
Change startup logic so secrets are loaded from the backend.
Pseudo-flow:
boot app
authenticate using workload identity / IAM / service account
fetch secret bundle
populate runtime config
start server
Step 5: Add rotation support
Plan for:
- secret versioning
- overlapping old/new credentials
- reload or restart when secrets change
- alerting if fetch fails
Step 6: Lock down local dev
For developer machines:
- use local
.envfor dummy values only - use a dev secrets store or encrypted
.envtool for real credentials - avoid sharing production secrets to laptops if possible
A secure pattern to follow
Bad
DB_PASSWORD=supersecret
JWT_SECRET=abc123
Better
DB_HOST=db.internal
DB_USER=appuser
SECRETS_PROVIDER=vault
SECRETS_PATH=prod/myapp
Then the app reads SECRETS_PATH and fetches the actual secret values at runtime.
What to build if you want your own replacement
If you’re building a tool/system, a solid minimal design is:
Components
-
Secret store
- encrypted at rest
- access controlled per app/team/environment
-
Auth layer
- workload identity, service accounts, OIDC, or short-lived tokens
-
API
- get secret by name/path
- list versions
- rotate/update
- audit access
-
Client
- CLI or SDK to retrieve secrets
- can generate a local
.envfor dev if needed
-
Audit/logging
- record who accessed what and when
- never log secret values
-
Rotation
- support versioned secrets
- allow old/new overlap
Basic storage model
secret_nameversionciphertextmetadataallowed_principalscreated_atexpires_at
Security must-haves
- encryption in transit and at rest
- strict authN/authZ
- audit logs
- secret redaction in logs
- short-lived credentials where possible
- least privilege access
- support revocation and rotation
If you want a simple implementation pattern
A common transition path is:
- build a small CLI:
mysecrets get DATABASE_URL
- use it in deploy scripts:
- fetch secrets during startup
- later move to runtime injection:
- app reads from a mounted file or SDK
This helps you replace hardcoded .env usage gradually without a big-bang rewrite.
Rule of thumb
- Development:
.envis okay for dummy/non-prod values - Production: use a secrets manager + runtime fetch/injection
- Best practice: never commit secrets; rotate them regularly; keep secrets out of app config files
If you want, I can help you design this for a specific stack like Docker Compose, Kubernetes, AWS, or Node/Python/Go.