Every AI-generated app we audit uses .env files as its secrets store. That's fine for a solo developer on a laptop. It stops being fine the moment your app has a second engineer, a CI pipeline, or a production deploy.
Here's why, and what to do about it.
What environment variables are actually good for
Environment variables were designed for configuration: things that change per environment but aren't sensitive. Database host. Log level. Feature flags. Public API URLs.
Somewhere along the way, the industry decided they were also fine for secrets. They're not — they're just marginally better than committing secrets to the repo, and the bar has moved.
Everything that reads your env
Take a moment and count everything that has access to process.env in a running Node app:
- Your own code
- Every dependency in your
node_modules - Every transitive dependency of every dependency
- Any postinstall script that ran during install
- Any error reporter that dumps env on crash (some do, by default)
- The process listing on the box (
ps auxein some configurations)
That is not a secrets store. That is a public bulletin board with a lock on the door.
The .env file specifically
The file itself is a plaintext copy of every secret your app uses, sitting on disk, usually next to the code. Things that go wrong with it:
- It gets committed to the repo by mistake (constantly).
- It gets copied into a Docker image and pushed to a public registry.
- It gets tar'd up with a backup and sent to somewhere less secure.
- It gets read by any process on the machine, in plaintext, no auth required.
What to graduate to
You don't need to boil the ocean. Here's a reasonable path:
Stage 1 — you have a .env file
Fine, for now. Do this:
- Add
.envto.gitignore. Verify it's not already in your git history withgit log --all --full-history -- .env. - Never share
.envin Slack. Use a secrets manager for handoff even if you're not using one for runtime. - Have a
.env.examplewith keys only, no values committed.
Stage 2 — a real secrets manager
Pick one and use it:
- AWS Secrets Manager or Parameter Store
- Google Secret Manager
- HashiCorp Vault
- 1Password Secrets Automation
- Doppler / Infisical if you want a hosted UI
Your app fetches secrets at boot from the manager. The manager handles rotation, audit logs, and access control per-identity.
Stage 3 — no long-lived secrets at all
The end state is that your app doesn't hold API keys — it holds a short-lived identity token (from IAM, OIDC, or a workload identity) and exchanges it for whatever access it needs, scoped tightly, with a TTL measured in minutes.
You don't have to be there tomorrow. But you should know that ".env file" is stage zero, not the finish line.
The rule of thumb
If you can't answer these questions in under thirty seconds, your secrets are not under control:
- Who has access to production API keys right now?
- When were they last rotated?
- If an engineer left the company today, what secrets would you have to rotate?
- If your
.envfile leaked to a public GitHub repo, what would you do in the first hour?
Every one of those has an answer. Your job is to make sure the answer isn't "I don't know."