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

  1. Commit .env.example only — never .env.
  2. Run gitleaks locally and in CI — see Keep secrets out of Git.
  3. 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:

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 .env gitignored 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

ProblemTooling
Prevent commits.gitignore, gitleaks, push protection
Share in team runtimesCI + cloud secret manager
Fast local access (Mac)PassStore + Keychain
Understand risksSecurity overview

Download PassStore for macOS


Deep dives