How to store API keys safely (without committing them)

“Safely” has two parts: (1) they never enter Git history, and (2) they are not trivially copied to every backup, chat window, and screenshot by accident. This article is a practical playbook — vendor dashboards change, but the order of operations stays the same.


1. Golden rules

  1. If it touched Git remotely, rotate — cleaning history is necessary but not sufficient. See I accidentally committed an API key.
  2. Separate test vs live — Stripe sk_test_ vs sk_live_, AWS dev account vs prod account, etc.
  3. Minimum scope — read-only tokens where possible; IP allowlists where the vendor supports them.
  4. Never embed server keys in frontend bundles — frontend leak guide.

2. Storage tiers (where keys should live)

LayerStore API keys here?Notes
Git repoNo (only .env.example placeholders)Practical Git guide
CI (GitHub Actions, GitLab, etc.)Yes — in masked secretsPrefer OIDC to clouds when possible
Production runtimeYes — platform secret managerAWS Secrets Manager, GCP Secret Manager, K8s Secret, etc.
Developer laptopYesvault or strict gitignored .envPrefer Keychain-oriented workflows

3. Git guardrails that actually catch mistakes

.gitignore

.env
.env.*
!.env.example

Local scan before push

gitleaks detect --source . -v

Install from gitleaks. Wire into pre-commit — same patterns as keep secrets out of Git.

GitHub push protection

Enable secret scanning and push protection for supported patterns. Official: About secret scanning.


4. The safest pattern on macOS for day-to-day dev

Canonical copy lives in a local-first vault (PassStore):

  • Encrypted at rest (AES-256-GCM; Argon2id key wrapping — Security).
  • Sensitive items can use Apple Keychain Services.
  • Workspaces mirror repos or products so you do not paste the wrong key.

Ephemeral use: copy into a shell session or a gitignored .env you treat as cache, not the system of record.


5. Worked example: new third-party API

  1. In the vendor UI, create a key named acme-api-dev-yourname (auditable naming).
  2. Paste into vault entry stripe_secret_key_dev (or your team convention from organize API keys).
  3. Add STRIPE_SECRET_KEY= to .env.example with a fake value.
  4. Locally: set real value only in vault → copy to .env or export.
  5. CI: store in STRIPE_SECRET_KEY secret; inject at runtime — never echo.

6. When you must share a key with a teammate

Avoid Slack and email. Prefer:

  • Per-person dev keys from the vendor.
  • Internal secret store your company already pays for.
  • Break-glass procedures with expiry.

Longer discussion: share environment variables safely with your team.


7. Rotation triggers (do not wait for “annual review”)

Rotate immediately when:

  • A key appeared in Git, logs, or tickets.
  • A teammate with access left the org (for shared keys).
  • You suspect malware or lost device.

Step-by-step: rotate API keys safely.


8. Soft CTA

Download PassStore for macOS if you want API keys off Slack and out of Git, with a workflow built for developers. For comparison shopping, see best secret management tools for developers in 2026.


Related