EMZETT.
Login

.env.local

In short: A special variant of the .env file for local, personal development environments — by convention (e.g. in Next.js) it’s automatically NEVER committed, even if .env itself were accidentally tracked.

In more detail: Many frameworks support several .env file levels with a clear priority (e.g. .env → .env.development → .env.local), with later files overriding earlier ones. .env.local is meant specifically for values that should only apply on your own computer (personal API test keys, local database URLs) and are never shared — unlike .env.example, which belongs in the repo as a template without real secrets.

In Depth

A typical loading order (using Next.js as an example) looks like this, with later files overriding earlier ones:

.env                    - base values, apply in all environments (may be committed if free of secrets)
.env.development        - only for a local `npm run dev`
.env.production         - only for the production build
.env.local              - overrides EVERYTHING above, is NEVER committed (standard .gitignore entry)
.env.development.local  - .env.local specifically for the development environment

The purpose of this layering: a team can commit sensible default values in .env/.env.development (e.g. a local test database URL that’s the same for everyone), while each person individually overrides differing, secret or personal values in .env.local — for example their own API test key for a third-party service, without this key ending up in the repository or being shared with colleagues.

A common beginner’s mistake: confusing .env.local with .env.example. .env.example is a template deliberately committed to the repository, with the same variable names but placeholders instead of real values (API_KEY=your_key_here) — it only serves as documentation of which variables a new team member has to set themselves in their own .env.local.

Why exactly this name was chosen as a convention

The naming convention of .local as a suffix isn’t a coincidence, but follows a pattern found across several frameworks and tools (not just for environment files, but also, for example, docker-compose.local.yml or settings.local.py in other ecosystems): the suffix clearly signals “this file only belongs to MY machine, not to the shared project state” — a signal developers understand immediately about which file may safely be committed and which not, without having to look into .gitignore every time.

Interaction with CI/CD environments

The distinction from environment variables in automated build/deployment pipelines (CI/CD) is important: there, secrets are generally NOT managed via any .env file in the file system, but directly via the respective platform’s “secrets” management (e.g. GitHub Actions secrets, Vercel environment variables in the project dashboard) — these values are only injected at runtime as real process environment variables, so they never appear as a file in the build server’s file system. .env.local is thus exclusively a tool for the local development experience on your own computer, not part of the actual deployment pipeline.

Typical pitfall: outdated local values

A practical problem in everyday team work: if a new environment variable is added in the code (e.g. a new API integration) but isn’t documented in .env.example, other team members often only notice when their local environment crashes with a cryptic “undefined” error, because their own .env.local doesn’t know the new variable yet. Well-run projects therefore consistently keep .env.example in sync with every new environment variable, often even automatically via a script that checks at start-up whether all variables listed in .env.example are actually set locally.

See also: .env files, Environment Variables