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