A developer pushes .env.production "temporarily" to unblock a colleague. Private repo — then consultant fork, leak in GitHub Actions logs, or repo made public by mistake. Active AWS and Stripe keys in git history forever. Reverting the commit does not revoke what bots already indexed.
Deployment secrets — API keys, database passwords, mail tokens — do not live in the repo. Full stop. The operational question is where they live and how they rotate without panic.
Non-negotiable rules
Put .env* in .gitignore and verify in CI that commits fail if a secret pattern is detected. Maintain a .env.example without values — documentation of required keys only. Plan rotation after departures, suspected leaks or end of contract. Apply least privilege: AWS key scoped to one action, not global admin. Never put secrets in URLs, Sentry, or support tickets.
| Anti-pattern | Replacement |
|---|---|
.env in git | Forge secrets + server file |
| Prod key in staging | Dedicated test accounts |
| Secret in Docker image | Runtime injection |
| Key shared via chat | Team password vault |
By environment
Local: each developer keeps a personal .env, never committed. CI: use encrypted GitHub or GitLab secrets; inject on deploy via SSH or platform API. Production server: /var/www/.env with deploy-only permissions, or systemd EnvironmentFile outside web root. Managed platform: encrypted dashboard env vars with audit trail. Pair with CI/CD for small projects without logging environment in verbose mode.
Proactive detection
Scan with gitleaks or trufflehog pre-commit or in CI. Enable GitHub secret scanning if the repo goes public. Quarterly audit of who has production key access.
Rotation without downtime
Use dual-active API keys when the provider allows (Stripe for example). For database: secondary user, switch connection string, revoke old one. Document the order — test in staging first.
The climax: a private repo is not a vault
See KMS key management for sensitive workloads beyond .env.
Decide and move forward without blind spots
First scan git history with gitleaks or equivalent. Revoke and rotate every exposed or doubtful key — not just the last commit. Centralise CI secrets and keep .env.example up to date. Strictly separate staging and production credentials. Train the team: never put secrets in tickets or chat. Compare platform hosts with documented secret management via the directory.
Frequently asked questions
.env committed by mistake?
Revoke and rotate immediately — git revert is not enough, history keeps the secret.
Secrets in CI?
Encrypted forge secrets, never echo in logs — even in debug.
Same .env staging and prod?
No — test and live keys separate, distinct cloud accounts if possible.
Is vault mandatory?
No for a small team; forge secrets plus server .env often suffice until many shared secrets.
Stop hiding keys in the repo means accepting that git is not a vault — and that rotation is a feature, not punishment.