Every developer has done it. Committed an API key to git, pushed to GitHub, and watched in horror as the secret scanner flagged it within minutes. If you’re lucky, the service revokes the key automatically. If you’re not, someone’s crypto-mining on your AWS account.
Secrets management isn’t glamorous, but getting it wrong is expensive.
The Problem Space
Secrets include:
- API keys and tokens
- Database credentials
- Encryption keys
- TLS certificates
- OAuth client secrets
- SSH keys
- Signing keys
These all share properties: they’re sensitive, they need rotation, and they need to reach your application somehow without being exposed.
Level 0: Don’t Do This
| |
Hardcoded secrets in source code. Committed to git. Available to everyone with repo access forever (git history persists).
This is where everyone starts. The goal is to not stay here.
Level 1: Environment Variables
| |
| |
Better. Secrets aren’t in git. But:
.envfiles get copy-pasted between developers- No audit trail of who accessed what
- Rotation means updating every deployment
- Secrets still exist as plaintext somewhere
For personal projects and early startups, this is often good enough. Know its limitations.
Level 2: Encrypted Secrets Files
| |
Tools like SOPS, git-crypt, or age encrypt secrets before commit.
Pros:
- Secrets live in git (versioned, auditable)
- Only people with the decryption key can read them
- Works offline
Cons:
- Key management for the encryption key itself
- Rotation still requires commits
- Everyone with the key sees all secrets
Level 3: Secrets Manager Services
AWS Secrets Manager, HashiCorp Vault, Google Secret Manager, Azure Key Vault—these are purpose-built secret stores.
| |
Pros:
- Centralized management
- Fine-grained access control (IAM)
- Audit logs
- Automatic rotation (for supported services)
- No secrets in git or environment
Cons:
- Network dependency at startup
- Cost (small, but non-zero)
- Vendor lock-in
- Complexity
Practical Patterns
The Startup Bootstrap Problem
Your secrets manager needs credentials to access. Those credentials are… secrets. It’s turtles all the way down.
Solutions:
- IAM roles (AWS): Instance/container gets permissions from its identity, no credentials needed
- Workload identity (GCP/K8s): Similar concept for Kubernetes
- Machine identity (Vault): Bootstrap with a one-time token
| |
Caching Secrets
Don’t fetch from the secrets manager on every request:
| |
Balance: Short TTL means more API calls but faster rotation pickup. Long TTL means fewer calls but stale secrets during rotation.
Rotation Without Downtime
The naive approach breaks:
- Rotate database password
- Old password stops working immediately
- Running applications fail until redeployed
The correct approach:
- Add new password (both old and new work)
- Deploy applications with new password
- Remove old password
For databases, this means:
| |
AWS Secrets Manager has built-in rotation Lambdas for RDS that handle this.
Configuration vs Secrets
Not everything sensitive is a secret. Consider:
| |
Mixing config and secrets in the same place causes:
- Overly restrictive access to non-sensitive config
- Treating non-secrets as secrets (unnecessary complexity)
- Bloated secret stores
Keep them separate.
The Twelve-Factor Approach
Twelve-Factor App says: store config in environment variables.
This is good advice with caveats:
- Environment variables are visible to the process and its children
- They appear in
/proc/<pid>/environon Linux - Crash dumps and debug logs might capture them
For truly sensitive secrets, consider:
- Fetching directly from a secrets manager
- Using short-lived credentials
- Memory-only storage with secure cleanup
| |
CI/CD Secrets
Your pipeline needs secrets too. Options:
GitHub Actions:
| |
Better—OIDC federation:
| |
No long-lived credentials. GitHub proves its identity, AWS grants temporary access.
What to Use When
Personal projects / early startup:
.envfiles (gitignored)- Maybe SOPS for shared secrets
Growing team:
- Cloud provider’s secret manager (AWS/GCP/Azure)
- IAM-based access control
- Basic rotation
Enterprise / compliance requirements:
- HashiCorp Vault or enterprise secret manager
- Fine-grained policies
- Audit logging
- Automated rotation
- HSM backing for crypto keys
Common Mistakes
Logging Secrets
| |
Sanitize logs. Secrets in logs end up in log aggregators, which end up in search indexes, which end up in breaches.
Secrets in Docker Images
| |
Docker image layers are permanent. Even if you delete the ENV in a later layer, it’s still in the history.
Secrets in Error Messages
| |
Error messages end up in bug trackers, error monitoring services, and support tickets.
Overly Broad Access
If everyone on the team can read production database credentials, you don’t have access control—you have shared passwords.
Principle of least privilege: developers get dev secrets, CI gets deployment secrets, production gets production secrets.
The Minimum Viable Security
If you do nothing else:
- Never commit secrets to git—use
.envfiles or a secrets manager - Use different secrets per environment—dev, staging, prod should never share credentials
- Rotate when people leave—assume departing employees have copies
- Log access, not content—know who accessed what, never log the actual secret
Secrets management isn’t exciting work. But a breach from a leaked API key is definitely exciting—in the worst way.
The best secret is one that doesn’t exist. The second best is one that’s properly managed.