guide8 min readupdated

What is a .env file? A developer's guide (2026)

A complete guide to .env files — what they are, how runtimes load them, why they matter, and how to use them safely across Node.js, Next.js, Django, and more.

TL;DR

A .env file is a plain-text file that stores environment variables — configuration values like database URLs, API keys, and feature flags — as KEY=VALUE pairs, outside your source code. Frameworks load these variables at runtime so the same code can run in development, staging, and production without modification.

Why environment variables exist

Twelve-Factor App principle III says: store config in the environment. The reason: config changes between deploys, but code shouldn't. A .env file is the local-dev-friendly way to follow that rule — every framework can read from process.env (or the equivalent), and every host (Vercel, Railway, Fly, AWS, GCP, Azure) can inject those same variables without shipping them in your build artifacts.

The format

A .env file is just newline-separated KEY=VALUE pairs:

# Example .env
NODE_ENV=production
DATABASE_URL=postgres://user:pass@localhost:5432/app
JWT_SECRET=a-very-long-random-string
DEBUG=true

A few rules every runtime agrees on:

  • Keys are case-sensitive and conventionally UPPER_SNAKE_CASE.
  • Values are strings by default — type coercion (to number, boolean) is the framework's job.
  • Lines starting with # are comments.
  • Empty lines are ignored.
  • Quotes around values are optional, but required when the value contains spaces or special characters.

How different stacks load it

Node.js

Load with the dotenv package (or Node 20+'s built-in --env-file flag). Access via process.env.KEY.

// package.json
"scripts": { "start": "node --env-file=.env server.js" }

// server.js
console.log(process.env.DATABASE_URL);

Next.js

Reads .env.local automatically. Variables prefixed with NEXT_PUBLIC_ are exposed to the browser; everything else is server-only. Load order: .env.env.local .env.development.env.production, with later files overriding earlier ones.

Python / Django

# settings.py
from dotenv import load_dotenv
load_dotenv()
DATABASE_URL = os.environ["DATABASE_URL"]

Ruby / Rails

Rails uses dotenv-rails. The gem auto-loads during boot; values land in ENV["KEY"].

Laravel

Laravel uses the vlucas/phpdotenv package under the hood. Access via env("KEY"), but only inside config files — never at runtime, because config is cached in production.

.env, .env.local, .env.example — what's the difference?

FilePurposeCommit to git?
.envBase defaults (sometimes checked in)Sometimes
.env.localYour machine's overridesNever
.env.exampleKeys without values — structure onlyAlways
.env.productionProd-specific valuesNever
.env.testTest-only valuesSometimes

Generate a safe .env.example from any .env if you haven't committed one yet.

.env file vs JSON, YAML, and Docker config

A .env file isn't the only way to store config — it's just the one nearly every loader understands natively. Here's how it stacks up:

FormatNestingNative loader supportBest for
.envFlat onlyUniversal — dotenv, Docker, most PaaS dashboardsSecrets, per-environment overrides
config.jsonNestedNeeds custom parsingStructured, non-secret app config
config.yamlNestedNeeds a YAML libraryHuman-edited config with comments
Docker environment:Flat onlyDocker/Compose nativeContainer-level overrides

If you're weighing the two directly, read .env vs JSON config or dotenv vs a config module for the full breakdown — or just convert an existing .env to JSON in the browser.

Why .env files break production

Because they're strings. And because they're usually edited by hand. The most common failures:

  • Missing key: process.env.API_KEY is undefined → NullPointerException at runtime.
  • Duplicate key: dotenv keeps the first; most other parsers keep the last. Silent divergence between local and prod.
  • Bad quotes: SECRET="abc swallows the rest of the file.
  • Committed secret: a .env accidentally checked in → secrets need rotation.

You can catch most of these with the ENV validator and the leak checker — both run in the browser.

FAQ

What is a .env file?

A .env file is a plain-text file, usually placed at the root of a project, that stores configuration as KEY=VALUE pairs — one per line. Applications read it at startup and load each pair into environment variables, keeping config like database URLs, API keys, and feature flags out of source code.

What does 'env' stand for?

'env' is short for 'environment' — as in environment variables, values the operating system or runtime exposes to a running process (accessed via process.env in Node.js, os.environ in Python, ENV in Ruby). A .env file is just a convenient, file-based way to set a batch of them for local development.

Is it safe to commit a .env file to git?

Not if it holds real secrets. Commit .env.example (keys only, no values) so teammates know what to set, and add .env, .env.local, and .env.production to .gitignore. If a real .env with secrets was ever committed, rotate every value it contained — removing the file later doesn't remove it from git history.

Can I use JSON or YAML instead of a .env file?

Yes — some teams prefer a config.json or config.yaml for structured or nested config. The tradeoff: .env is the format every 12-factor-style loader (dotenv, docker --env-file, most PaaS dashboards) understands natively, while JSON/YAML need custom parsing code. See our full comparison of .env vs JSON config for the detailed tradeoffs.

What happens if a required .env variable is missing?

Most runtimes don't error by default — process.env.MISSING_KEY simply returns undefined in Node.js, and the failure surfaces later as a confusing crash (e.g. connecting to a database with an undefined URL). The fix is validating required keys on boot — see 'Why .env files break production' below.

What comes next

Once you've got a valid .env, the next question is how to manage it across machines and environments. Our .env best practices guide covers the 10 rules that matter most.

Related tools

Continue reading