Post

.env Files — Multiple Environments

The core concept

.env files store environment-specific config — API keys, database URLs, feature flags — outside of your code. You never commit them. The environment (local, staging, prod) loads its own values.


File hierarchy

Most frameworks follow this load order (later files override earlier):

1
2
3
4
.env                  # base defaults, committed (no secrets)
.env.local            # local overrides, gitignored
.env.[environment]    # e.g. .env.production, committed (no secrets)
.env.[environment].local  # local overrides per env, gitignored

What to commit:

  • .env — safe defaults only, no secrets
  • .env.example — template showing all required keys with blank/fake values

What to gitignore:

  • .env.local
  • .env.*.local
  • .env.production if it contains real secrets

Next.js env hierarchy

Next.js loads these in order, later wins:

1
2
3
4
.env
.env.local
.env.development / .env.production / .env.test
.env.development.local / .env.production.local / .env.test.local

NEXT_PUBLIC_ prefix exposes the var to the browser. Without it, server-side only.

1
2
3
4
5
6
# .env.local (gitignored)
DRUPAL_GRAPHQL_URL=http://mysite.ddev.site/graphql

# exposed to browser
NEXT_PUBLIC_ALGOLIA_APP_ID=your_app_id
NEXT_PUBLIC_ALGOLIA_SEARCH_KEY=your_search_key

Use NEXT_PUBLIC_ENVIRONMENT to switch index names or API targets per env:

1
export const indexName = (process.env.NEXT_PUBLIC_ENVIRONMENT || "prod") + "_search"

Do .env files relate to git branches?

Yes — indirectly but directly in practice.

The .env file itself never leaves your machine (gitignored). But your git branch determines which deployment environment is triggered, and that environment injects its own set of variables.

1
2
3
main branch      → production deployment  → production env vars injected
develop branch   → staging deployment     → staging env vars injected
feature/x branch → preview deployment     → preview env vars injected

Vercel

Vercel has three env scopes tied to branches:

ScopeBranch
Productionmain (or custom)
Previewall other branches
Developmentlocal vercel dev

Set vars per scope in the Vercel dashboard. The correct set is automatically injected when that branch deploys.

GitHub Actions

Use GitHub Environments to scope secrets per branch:

1
2
3
4
5
6
# .github/workflows/deploy.yml
jobs:
  deploy:
    environment: production   # or staging
    steps:
      - run: echo $

Branch protection rules control which branches can access which environment’s secrets.

Netlify

Same pattern — set env vars per deploy context (production, deploy-preview, branch-deploy) in site settings.


DDEV (local)

DDEV has its own env layer. Add vars to .ddev/config.yaml:

1
2
web_environment:
  - MY_VAR=value

Or use a .env file in your project root — DDEV picks it up automatically for ddev exec and web container context.


WordPress

WordPress doesn’t use .env natively, but wp-config.php can be made env-aware:

1
2
3
4
5
// wp-config.php
define('DB_NAME', getenv('DB_NAME') ?: 'local_db');
define('DB_USER', getenv('DB_USER') ?: 'root');
define('DB_PASSWORD', getenv('DB_PASSWORD') ?: '');
define('WP_DEBUG', getenv('WP_ENV') === 'production' ? false : true);

With DDEV, set the vars in .ddev/config.yaml. On the server, set them in the Apache/Nginx vhost or via a .env file loaded by a package like vlucas/phpdotenv.


.env.example — always commit this

Document every required key with a blank or fake value:

1
2
3
4
5
6
7
8
# .env.example
DRUPAL_GRAPHQL_URL=
NEXT_PUBLIC_ALGOLIA_APP_ID=
NEXT_PUBLIC_ALGOLIA_SEARCH_KEY=
NEXT_PUBLIC_ENVIRONMENT=local
DB_NAME=
DB_USER=
DB_PASSWORD=

New developers copy this, fill in real values, and never commit the result.


Summary

FileCommittedPurpose
.envYesSafe base defaults
.env.exampleYesTemplate for onboarding
.env.localNoLocal secrets/overrides
.env.productionDependsProd config (no secrets)
.env.production.localNoLocal prod overrides

CI/CD (Vercel, Netlify, GitHub Actions) bridges the gap between branches and env vars — your branch signals the environment, the platform injects the right vars.

This post is licensed under CC BY 4.0 by the author.