guide7 min readupdated 2026-04-17

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 — 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.

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.

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