Environment Variables vs Secrets
Environment variables describe how a value is delivered to a process; secrets describe what kind of value it is — sensitive credentials that grant access.
Definition
An environment variable is a named value the operating system makes available to a running process. It is a delivery mechanism — a standard, language-agnostic way to pass configuration into an app at startup without baking it into the code. A secret is a sensitive value that grants access and must stay confidential. These are different categories: one describes how a value is delivered, the other describes what kind of value it is.
The confusion is understandable, because secrets are very often delivered as environment variables. The twelve-factor convention of reading config from the environment made DATABASE_URL and STRIPE_SECRET_KEY both arrive the same way. But equating the two is what leads teams to store a private key in a plaintext .env file and treat it as casually as a log level. Every secret can be an environment variable; not every environment variable is a secret.
Why it matters
Treating secrets like ordinary environment variables imports the weaknesses of the environment as a storage medium. A plaintext .env file sits unencrypted on disk, gets copied to teammates over Slack, lands in backups, and is one stray git add . away from a public commit. Environment variables are also readable by every process the user runs — including a compromised dependency or a prompt-injected AI coding agent — so a secret in the environment is exposed to far more than the one app that needs it.
Drawing the line deliberately changes how you handle each. Plain config can live in the repo, in plaintext, reviewed like any other code. Secrets need encryption at rest and in transit, access control, an audit trail, rotation, and revocation. Lumping them together forces you to either over-protect harmless config or under-protect real credentials. Splitting them lets you apply the right level of care to each.
Examples
The same .env file usually mixes both. Sorting it is the whole exercise:
- Environment variable, not a secret:
NODE_ENV=production,LOG_LEVEL=info,AWS_REGION=us-east-1,PORT=3000,FEATURE_NEW_CHECKOUT=true. Leaking these costs nothing; they can sit in the repo. - Secret, delivered as an environment variable:
DATABASE_URL=postgres://user:pass@host/db,STRIPE_SECRET_KEY=sk_live_...,JWT_SIGNING_KEY=...,AWS_SECRET_ACCESS_KEY=.... Same mechanism, but each grants access and must be protected.
A useful test: imagine the value printed in a CI log that any teammate can read. If that is fine, it is config. If it would trigger an incident and a rotation, it is a secret — regardless of the fact that it is delivered through process.env.
Related terms
- Secrets in DevOpsCredentials like API keys, passwords, and tokens that grant access and must stay confidential — and how they differ from config.
- Secret RotationReplacing credentials on a schedule to cap their exposure — the patterns, the cutover problem, and how automation helps.
- SAML for Secrets ManagementHow single sign-on governs who can reach your secrets — and why identity is a separate guarantee from encryption.
How Capy helps
Capy keeps the ergonomics of environment variables while removing the plaintext-on-disk problem. Instead of a static .env file, secrets are stored as ciphertext encrypted on your machine, and capy run -- your-command injects them into the process environment only for the duration of that command. Your app still reads process.env.STRIPE_SECRET_KEY exactly as before; the difference is that the value was never sitting in a readable file waiting to leak.
Because the encryption happens client-side, the service stores ciphertext it cannot decrypt, and a value that is not actively being injected stays as ciphertext on disk — out of reach of an unrelated process or a compromised agent. For the broader picture of what counts as a secret in the first place, see what are secrets in DevOps, and for keeping them fresh over time, see secret rotation.
Frequently asked questions
Is an environment variable the same as a secret?+
No. An environment variable is a way to pass a value into a process at startup. A secret is a sensitive value that grants access. Secrets are commonly delivered as environment variables, but plenty of environment variables — a region name, a log level — are not secrets at all.
Is it safe to store secrets in a .env file?+
A plaintext .env file is convenient but risky. It sits unencrypted on disk, is easy to commit by accident, gets shared over chat, and is readable by any process the user runs. It is acceptable for local throwaway values, but real credentials should be encrypted and injected at runtime rather than left in a static file.
How do I tell config from secrets in my .env?+
Ask whether leaking the value would cause harm. If it could appear in a public CI log without consequence — a port number, a feature flag, a region — it is config. If leaking it would force you to rotate and investigate, it is a secret. Config can live in the repo; secrets need encryption, access control, and rotation.
Can I keep using process.env if I move secrets out of .env?+
Yes. Tools like Capy inject secrets into the process environment at runtime with a command wrapper, so your code still reads process.env as usual. The value is decrypted only for that process and is never written to a long-lived plaintext file.
Install Capy in 30 seconds
brew install capysc/tap/capynpm i -g @capy/cli