"Where should I put my API keys?" is the single most-Googled question by new developers. The answer depends on the tier — local, staging, production — and the blast radius if a key leaks. Here's the concrete playbook we'd hand a new hire on day one.
The three rules
- Never in source code. Even in a private repo. One accidental commit to public = rotation.
- Never the same key in two tiers. If your dev key leaks, it should not touch production.
- Always rotatable. If you can't rotate a key in < 5 minutes, you're one push away from an incident.
Local development
Use a .env.local file, git-ignored. Commit .env.example with the keys (no values) so new hires know what to fill in — generate one in the browser from your existing file.
# .gitignore
.env
.env.local
!.env.example
# .env.example (committed)
OPENAI_API_KEY=
STRIPE_SECRET_KEY=
DATABASE_URL=
# .env.local (NOT committed)
OPENAI_API_KEY=sk-...
STRIPE_SECRET_KEY=sk_test_...
DATABASE_URL=postgres://localhost/appUse test-mode keys only — Stripe sk_test_, OpenAI with a hard spend cap, throwaway DB credentials. Your laptop drive is not as secure as you think.
If you're on a shared dev machine
Don't put secrets on disk. Use direnv or your shell's keychain instead. On macOS: security add-generic-password. On Linux: secret-tool store.
Staging
Use your host's env variable dashboard — Vercel, Railway, Fly, Netlify, Render all have one. Never reuse staging keys in production and vice versa.
Staging should hit a separate account at every third party: separate Stripe test-mode account, separate Supabase project, separate S3 bucket. One staging bug shouldn't be able to touch a single byte of production.
Production (the one that matters)
Three acceptable options, in order of sophistication:
1. Host-native env vars (simplest)
Set keys in Vercel's / Railway's / Fly's dashboard. The platform injects them at runtime. Zero infra to manage.
When it's enough: solo projects, < 10 keys, one environment.
When it breaks down: multiple services sharing keys, audit requirements, key rotation across many apps.
2. A dedicated secrets manager (recommended for teams)
The top picks in 2026:
- Doppler — great UX, pulls into your app at runtime. Free tier is generous. Partner link: doppler.com.
- Infisical — open-source, self-hostable. Cheaper for larger teams.
- 1Password Secrets Automation — if your team already uses 1Password, this is zero-friction.
- AWS Secrets Manager / GCP Secret Manager / Azure Key Vault — the cloud-native option. Required if you're already all-in on that cloud.
3. Hashicorp Vault (for regulated shops)
Dynamic secrets, fine-grained policy, audit logs. Not for weekend projects — run it only if you have the staff to maintain it.
The rotation checklist
Every secret storage choice needs to answer: can I rotate in under 5 minutes? Test it before you need to.
- [ ] Can I rotate a single key without redeploying every service?
- [ ] Does my app pick up new values without a restart?
- [ ] Do I have an audit log of who accessed which secret when?
- [ ] Can I revoke a leaked key within 60 seconds?
If any answer is no, you're one incident away from a 2am scramble.
Detection — so you find out first
Never rely on Google or GitHub to tell you a key leaked. Instead:
- Run the leak checker on every
.envyou're about to share. - Install gitleaks or trufflehog as a pre-commit hook.
- Enable GitHub's secret scanning (free on all public repos).
- Pipe host logs to a SIEM and alert on
sk_live_,AKIA, andghp_patterns in stderr.
TL;DR
- Local:
.env.local, git-ignored, test-mode keys only - Staging: host dashboard, separate third-party accounts
- Production: dedicated secrets manager (Doppler / Infisical / 1Password / cloud-native)
- Every tier: rotation drill quarterly, pre-commit leak scan
And when you catch a leak: rotate, scrub history, audit access — the rotation playbook is here.