Missing environment variables on deploy

The app boots locally but crashes or misbehaves once deployed, because the values it reads from the environment aren't there.

The symptom

After deploying you see undefined where a config value should be, a crash on startup like KeyError: 'API_KEY' or Cannot read properties of undefined, or a feature that silently does nothing. Locally it all worked.

Why it happens

Locally your app reads values from a .env file. That file is — correctly — not committed to GitHub, so it never reaches production. Nothing sets those variables on the server unless you do. The app starts with them missing.

The fix

Set every variable your app needs in the dashboard, by kind:

  • Plain config (feature flags, public URLs, modes): set as environment variables in the dashboard.
  • Secrets (API keys, tokens, passwords): set them in the Vault. They're encrypted and injected into your app at runtime, so they never live in your repo.

Read them the same way you do locally — process.env.NAME in Node, os.environ["NAME"] in Python. Use the exact same names as your .env file; a typo or case mismatch reads as missing.

Use your .env.example as the checklist: every key in it needs a value set in the dashboard. Keep committing .env.example (names only, no values) and keep .env in .gitignore.

The one that catches frontend apps: browser variables

Vite and Next.js inline browser-visible variables into the bundle when the app is built — and the build doesn't see your dashboard variables. So setting a VITE_ or NEXT_PUBLIC_ value in the dashboard does nothing; the build never reads it. What to do instead depends on the framework:

  • Vite single-page apps: a static build has no server, so it can't read the dashboard at all. To configure it from the dashboard, use runtime config — write the values into the page when the container starts and read window.__APP_CONFIG__. The full-stack recipe shows the setup.
  • Next.js: server code reads dashboard variables at runtime, which covers most config. Only browser values need the NEXT_PUBLIC_ prefix — and since the build can't see the dashboard, set those in code (e.g. next.config.js), for public values only.

Never put a real secret behind VITE_ or NEXT_PUBLIC_ — anything with that prefix is shipped to every visitor's browser. Secrets stay server-side, in the Vault.

Set by Dockhold — don't define these

PORT and DATABASE_URL are injected for you. Read them; don't set or override them. If DATABASE_URL is missing, you haven't enabled the managed database for the app yet.

Related