A practical developer secret management setup for 2026
This is a single end-to-end system you can adopt incrementally. It is not the only valid design — but it is coherent: each layer reinforces the others instead of fighting.
If you want the same ideas as a checklist per repository, start with where to store .env files securely and structure environment variables in large projects.
Layer 0 — Culture (free, hard)
- No secrets in chat. Ever. Use ticket attachments only when your security team says so — and prefer redacted logs.
- No “quick” prod access. Break-glass only, time-boxed, audited.
- Rotate after doubt. Cleaning Git history does not un-steal a key.
Layer 1 — Git and repositories
- Commit
.env.exampleonly — never.env. - Run
gitleakslocally and in CI — see Keep secrets out of Git. - Enable GitHub secret scanning + push protection where available.
If something slipped through: I accidentally committed an API key.
Layer 2 — Continuous integration
- Store secrets in CI provider secrets (GitHub Actions
secrets, etc.). - Mask logs; never
echo $MY_SECRET. - Prefer OIDC to cloud providers over long-lived cloud keys in CI when possible.
GitHub Actions: OIDC to AWS (pattern)
Long-lived AWS_ACCESS_KEY_ID in GitHub org secrets is a common foot-gun. OIDC lets the workflow assume an IAM role without storing static cloud keys in GitHub. AWS documents the trust policy and role setup in Configuring OpenID Connect in Amazon Web Services.
Conceptual workflow snippet (adapt to your role ARN and region):
# .github/workflows/deploy.yml (illustrative — verify against current GitHub/AWS docs)
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsDeployRole
aws-region: us-east-1
# ... aws cli / CDK / Terraform here — no static keys in repo
GitLab CI variables
Use masked and protected variables for production; restrict who can trigger protected branches. See GitLab docs on CI/CD variables.
Never do this in CI
# BAD — may end up in logs, artifacts, or support bundles
echo "DATABASE_URL=$DATABASE_URL"
npm run debug:print-config
If you must debug, log names and hashes of config presence, not values:
test -n "$DATABASE_URL" && echo "DATABASE_URL is set (len=${#DATABASE_URL})"
Layer 3 — Production and staging
- Use your platform’s native secret store (AWS/GCP/Azure/K8s).
- IAM scopes: smallest permission that works.
- Separate accounts or projects for prod vs non-prod when feasible.
Layer 4 — Local development (macOS)
This is where local-first tools earn their keep:
- Keep canonical dev keys in a vault (PassStore) with workspace grouping.
- Copy into shells or tool configs explicitly — see Organize API keys without slowing down.
- Understand Keychain benefits vs plaintext
.env: macOS Keychain for developers.
Multi-service layout: multiple .env files.
Layer 5 — When to add Vault / Doppler / enterprise SaaS
Add centralized team tooling when you need:
- Organization-wide audit on secret access.
- Automated rotation with vendor hooks.
- Dynamic secrets (short-lived DB users, etc.).
Read the trade-offs: Local-first vs cloud secret managers.
Weekly habits (10 minutes)
- Skim CI failures for accidental logging.
- Delete old API keys you no longer use in vendor dashboards.
- Confirm new repos have
.envgitignored and an example template.
Monthly habits (30 minutes)
- Rotate shared dev keys if your team shares them (better: stop sharing; use per-dev keys).
- Review GitHub org secret scanning alerts.
- Audit backup paths that include project folders.
The minimalist tool list
| Problem | Tooling |
|---|---|
| Prevent commits | .gitignore, gitleaks, push protection |
| Share in team runtimes | CI + cloud secret manager |
| Fast local access (Mac) | PassStore + Keychain |
| Understand risks | Security overview |