Keychain vs .env files for developers (complete comparison)

Both solve “get a secret into process.env eventually.” They differ in how secrets live between runs: OS-managed encrypted storage vs plaintext files next to your code.


1. Quick comparison

Dimension.env fileKeychain (direct or via app)
At-rest in project treePlaintextNot in repo folder
Accidental zip / syncHigh riskLower
ErgonomicsTrivial catNeeds app or security CLI
CI serversCommonRare (use CI secrets)
Cross-platformYesmacOS / Apple ecosystem
Team syncDangerousPer-device or wrapped export

Apple docs: Keychain Services


2. Threat model translation

  • Casual file theft (copy folder to USB): Keychain wins.
  • Malware on unlocked Mac: Neither is a strong boundary — assume compromise; reduce scope and lifetime.
  • Git leak: Keychain wins indirectly (secret not in tree) — still need Git hygiene.

3. Why not raw Keychain for everything?

Ad-hoc security add-generic-password scripts scale poorly:

  • Hard to audit what you created six months ago.
  • Easy to log secrets accidentally.
  • Access groups are easy to get wrong.

Prefer a developer vault that uses Keychain under the hood: PassStore · Security.


4. Hybrid pattern (recommended)

  1. Store canonical values in PassStore / Keychain-backed vault.
  2. Inject into environment for runtime:
export STRIPE_SECRET_KEY="$(security find-generic-password -s STRIPE_SECRET_KEY -w)"
# Prefer GUI vault copy over scripting for routine work
npm run dev
  1. Gitignore any generated .env if your tooling requires a file.

5. When .env is still reasonable

  • Purely local test keys with low blast radius.
  • Docker Compose env_file workflows with short-lived files.
  • Teaching demos — never real keys.

Read: is .env safe?


Related