← Back to Home

Secrets Management — Stop Committing Passwords to Git

Visual guide to secrets management for developers. Compare Vault, AWS Secrets Manager, Doppler, and SOPS. Understand rotation, architecture patterns, and common anti-patterns.

You have passwords in your Git history. I don’t know your team, but statistically, I’m right. GitHub’s secret scanning team reports that over 100 million secrets were leaked in public repositories in 2024 alone. Private repos aren’t much better — they’re just harder to scan externally.

Secrets management isn’t glamorous. It’s not a conference talk topic. But it’s the difference between “we had a minor incident” and “we had a breach that made the news.”

1. How Secrets Actually Leak

Nobody wakes up and decides to commit their production database password to GitHub. It happens gradually, through convenience shortcuts that become security habits. Each one feels harmless in isolation. Together, they create attack surface.

Where Secrets Go to Die

Every breach investigation starts with one of these.

Hardcoded in source codeCommitted to Git, visible to every developer, persists in history forever even after deletion
.env files committed to repo".env added to .gitignore" — cool, except 8 months ago someone committed it and you never noticed
CI/CD environment variables (unmasked)Echo'd in build logs. Visible in pipeline UI to anyone with repo access. Leaked in error messages.
Shared in Slack/email"Hey can you send me the prod DB password?" — now it's in Slack search forever. And in backups. And in exports.
Same secret everywhereDev, staging, and production use the same credentials. One compromised environment = all environments compromised.

The uncomfortable truth: git rm secrets.env doesn’t remove it from history. The file is still in your Git history, forever, in every clone. Cleaning Git history requires git filter-branch or BFG Repo-Cleaner — and even then, any previous fork or clone still has it. Prevention is 1000x easier than cleanup.

2. Pick Your Tool

There’s no shortage of secrets management tools. The right choice depends on your scale, cloud provider, and how much operational complexity you can absorb. Here’s an honest comparison:

Tools — Pick Your Vault

HashiCorp VaultSelf-hosted / HCP

The gold standard. Dynamic secrets, automatic rotation, lease-based access. Handles every secret type (DB creds, PKI certs, cloud IAM, SSH keys). Complex but comprehensive.

Dynamic secretsAuto-rotationOperational complexity
AWS Secrets ManagerManaged

Tight AWS integration. Automatic rotation for RDS/Redshift. IAM-based access control. Simple but locked to AWS ecosystem.

Zero opsRDS auto-rotateAWS only
DopplerSaaS

Developer-friendly secret syncing. Manages .env replacements across environments. Good DX, weak on dynamic secrets.

Great DXMulti-cloudNo dynamic secrets
SOPS + Age/KMSGitOps-native

Encrypt secrets in Git. Decrypt at deploy time. Works with FluxCD/ArgoCD. Secrets live with code (encrypted). Simple, GitOps-friendly.

Git-nativeNo extra serviceNo rotation

My recommendation for most teams: if you’re on AWS, start with AWS Secrets Manager (zero ops, auto-rotation for RDS). If you’re multi-cloud or need dynamic secrets, invest in Vault. If you’re a small team that just needs to stop using .env files, Doppler gets you from zero to managed in an afternoon.

3. Rotation — Because Secrets Expire

A secret that never rotates is a ticking time bomb. If it was leaked 6 months ago and you don’t know it, the attacker has had 6 months of access. Rotation limits the window of exposure — even if a secret leaks, it’s only valid until the next rotation.

Rotation — The Forgotten Step

A secret that never changes is a secret waiting to be exploited. Rotation limits blast radius.

1
Generate new secretVault/SM creates a new credential pair
2
Update target systemNew password set on database/service
3
Dual-active windowBoth old and new work for grace period (rolling deploy)
4
Revoke old secretOld credential invalidated after all consumers updated
Recommended rotation frequency:
API keys: 90 days DB passwords: 30 days (or use dynamic per-request) TLS certs: Auto via ACME/cert-manager After incident: Immediately, all secrets potentially exposed

The key insight: rotation must be automated. Manual rotation means it never happens. “Rotate DB password every 90 days” turns into a Jira ticket that gets deprioritized for 6 quarters. Automate it or accept it won’t happen.

4. The Architecture

The right pattern is simple: applications never contain secrets. They request secrets from a centralized secrets manager at runtime, use short-lived credentials, and those credentials get rotated automatically.

The Right Architecture

Applications should never store secrets. They should REQUEST them at runtime.

Applications
API Service
Worker
Cron Job
Request secret
at startup
Secrets Manager
Vault / AWS SM
ACL: who can read whatAudit: every access loggedEncrypt: at rest + in transitRotate: automatic schedule
Short-lived
credentials
Target Systems
PostgreSQL
Redis
S3 / Cloud APIs

The best implementation uses workload identity: your Kubernetes pod authenticates to Vault using its service account token. No static credentials needed anywhere. The pod proves who it is, Vault gives it a short-lived database password (30 minutes), the pod uses it and requests a new one before it expires. Zero secrets stored.

5. Getting Started

If you’re reading this and your team stores secrets in .env files or CI/CD variables, here’s the migration path. Don’t try to fix everything at once — start with the highest-risk secrets (production database, payment API keys, admin tokens) and work down.

Implementation Checklist

Run `git log --all -p | grep -i "password\|secret\|api_key"` — clean history if needed
Add pre-commit hook with gitleaks or trufflehog — block secrets before they're committed
Migrate all .env secrets to Vault / AWS Secrets Manager / Doppler
Unique secrets per environment — dev, staging, prod NEVER share credentials
Enable audit logging — know WHO accessed WHAT secret WHEN
Set up automatic rotation with dual-active grace period
Least privilege — services only access secrets they need. No wildcard policies.
Incident playbook — if a secret leaks, rotate ALL secrets in that scope within 1 hour

The first step costs nothing: install a pre-commit hook that blocks secrets from being committed. Tools like gitleaks and trufflehog catch 95% of accidental secret commits. That alone prevents the most common leak vector. Do it today — it takes 5 minutes and costs zero dollars.