All guides

LEARN \u00b7 DEBUGGING GUIDE

Environment variable undefined in production: how to debug it

The app starts, tries to read an environment variable, and gets `undefined`. It worked fine five minutes ago on your machine. The variable exists — the app just cannot see it.

BeginnerWorks locally, fails in production

What this usually means

The variable is not being injected into the runtime process. This can happen for many reasons: the variable is set in a `.env` file that is not committed or not read in production, the deployment platform uses a different variable name format, the variable is set at build time but not available at runtime, or a secret manager has a typo in the key name. The variable name itself might also have a subtle difference — trailing space, wrong case, or an unexpected prefix.

( 01 )Fast diagnosis

The first ten minutes \u2014 establish facts before touching code.

  • 1Add a temporary startup log that prints all env var keys the app can see (not values — never log secrets). Check if the key exists at all.
  • 2Verify the variable name character by character. `DATABASE_URL` is not the same as `DATABASE-URL`, `database_url`, or `DATABASE_URL ` (trailing space).
  • 3Check the deployment platform's environment variable UI or config. Confirm the variable is set for the correct environment (production, not preview/staging).
  • 4If using Docker, check that the variable is passed via `--env`, `--env-file`, `docker-compose.yml`, or the orchestrator's config — not just in a local `.env`.
  • 5Check if the variable is set at build time (`NEXT_PUBLIC_*` prefix in Next.js, `VITE_*` in Vite) vs runtime. Build-time vars are baked into the bundle and unavailable at runtime.
( 02 )Where to look

The specific files, logs, configs, and dashboards that usually own this bug.

  • searchDeployment platform dashboard (Vercel, Render, Railway, Heroku) — environment variables section
  • searchCI/CD pipeline environment variable settings (GitHub Actions secrets/vars, GitLab CI variables)
  • searchDockerfile `ENV` directives and `docker-compose.yml` `environment` / `env_file` sections
  • searchKubernetes ConfigMap and Secret manifests
  • searchApplication startup logs — print `Object.keys(process.env)` (filter secrets) to see what is visible
  • search`.env` file and `.env.example` — compare the key names
  • searchSecret manager (AWS Secrets Manager, HashiCorp Vault, Doppler) — key name and access policy
( 03 )Common root causes

Practical causes, not theory. These are the things you will actually find.

  • warningVariable is set in the wrong environment scope (preview vs production)
  • warningVariable name has a typo, wrong case, or extra whitespace
  • warningVariable is a build-time-only variable but the app reads it at runtime
  • warningDocker container does not receive the variable because `env_file` is not mounted or `--env` is missing
  • warningCI/CD pipeline has the variable but the deployment step does not forward it to the runtime
  • warningSecret manager access policy blocks the runtime from reading the value
  • warningVariable is defined in `.env` but `.env` is not copied into the Docker image or production filesystem
( 04 )Fix patterns

Concrete fix directions. Pick the one that matches your root cause.

  • buildAdd a startup preflight that checks all required env vars and fails fast with a clear message listing which ones are missing
  • buildUse a consistent variable naming convention (`UPPER_SNAKE_CASE`) and validate keys against a known list
  • buildLog the count of loaded env vars at startup (not values) — if it is lower than expected, injection failed
  • buildMove all production env vars to the deployment platform's native variable system — do not rely on `.env` files in production
  • buildFor Docker, use `docker run --env-file` or the orchestrator's built-in secret/env injection, not baked-in `ENV` in the Dockerfile
( 05 )How to verify

A fix you cannot prove is a guess. Close the loop.

  • verifiedDeploy with the preflight check enabled and confirm startup succeeds.
  • verifiedCheck the startup log shows the expected number of loaded env vars.
  • verifiedManually trigger a request that uses the variable and confirm it returns the expected result.
  • verifiedRemove the variable from the platform config, confirm the preflight fails with the correct error message, then restore it.
  • verifiedTest in a staging environment with the same variable injection method as production.
( 06 )Mistakes to avoid

Things that make this bug worse or harder to find.

  • warningLogging the actual env var values in startup or error messages
  • warningHardcoding a fallback value instead of fixing the injection gap
  • warningAssuming the variable name is correct without checking for invisible whitespace
  • warningSetting variables in multiple places and losing track of which one actually applies
  • warningUsing different variable names for the same thing across environments