All guides
Reference/ 8 min read/

Environment Variables and Secrets on a Modern PaaS

How environment variables and secrets actually work on Launchverse — build-time vs runtime, NEXT_PUBLIC vs server-only, rotation strategy, and the .env file you should never commit.

Environment variables sound boring. They are responsible for an outsized share of production incidents anyway — secrets leaked to the client bundle, stale keys never rotated, dev databases pointed at by production by accident. This guide covers how env vars actually work on Launchverse, what differs from local development, and how to manage secrets without becoming a story on Krebs.

What env vars are actually doing

Most languages expose environment variables through a runtime call (process.env.FOO in Node, os.getenv("FOO") in Python, etc.). The variables themselves come from one of three places:

  1. The shell that spawned the process (locally: your terminal; on Launchverse: the container init).
  2. A .env file loaded explicitly by your application code (typically only in development).
  3. Build-time replacement during the build step — for languages where the build emits a static bundle (Next.js, Vite, etc.), specific variables are baked into the bundle at build time and frozen there.

The third category is where mistakes happen. Build-time variables can't be changed without a rebuild. They also leak to the browser if you mark them as public.

Build-time vs runtime — the critical distinction

For static-rendered or client-bundled frameworks:

Variable typeAvailable atCan be changed without rebuild?Visible to browser?
NEXT_PUBLIC_* (Next.js)Build-timeNo — must redeployYes
VITE_* (Vite)Build-timeNo — must redeployYes
REACT_APP_* (Create React App)Build-timeNo — must redeployYes
All other vars (Next.js / Vite / etc.)Runtime, server-onlyYes — restart containerNo

The rule is: if the variable name starts with NEXT_PUBLIC_, VITE_, or REACT_APP_, it's compiled into client JS and is publicly visible. Treat it as if you printed it on a billboard.

For backend frameworks (Express, Django, Rails, Phoenix, FastAPI, etc.), there is no build-time freezing — every env var is a runtime read, and changing one in Launchverse + restarting the container picks up the new value immediately.

What goes where

A good default placement strategy:

WhereWhat goes hereExample
Launchverse env vars (production)Real production secrets, third-party API keys, prod DB URLSTRIPE_SECRET_KEY, DATABASE_URL
Launchverse env vars (preview / staging)Staging/preview equivalents, test API keysSTRIPE_SECRET_KEY=sk_test_...
Local .envLocal dev values (never commit this file)DATABASE_URL=postgres://localhost/myapp
.env.example (committed)List of required variable names with placeholder valuesSTRIPE_SECRET_KEY=sk_test_...

The .env.example file is your living documentation. Commit it. Don't commit .env.

Setting variables on Launchverse

For each project: Settings → Environment Variables. Variables can be added, edited, and removed at any time. The platform restarts the container on save so the new values take effect.

For build-time variables (the NEXT_PUBLIC_* family), you must also click "Rebuild" — the values are baked into the JS bundle, not read at runtime, so changing them in Launchverse without rebuilding has no effect.

A trick most teams miss: you can paste a .env-format block into the bulk editor. Newline-separated KEY=value lines parse correctly. Saves dozens of clicks when copying secrets across projects.

Multi-environment setup

Real teams run at least three environments: development, staging, production. The clean pattern:

  • One Launchverse project per environment. Don't try to multiplex; the routing and secret separation is worth the duplicated config.
  • Identical variable names, environment-specific values. Your code reads DATABASE_URL everywhere; the value differs per project.
  • Different OAuth credentials per environment. Don't share Stripe / Google / Auth0 credentials across staging and prod. A bug in staging shouldn't be able to charge real cards.

For PR previews specifically, the project's environment variables are inherited by every preview unless you override per-PR. The default is good enough for 95% of PR previews — they're using the staging DB with the staging Stripe key.

Rotating secrets

Secret rotation is the single most-skipped security practice. The reasons people skip it (fear of breaking production) are addressable:

  1. Generate the new secret in the upstream provider (Stripe dashboard, AWS IAM, etc.). For most providers, you can have both old and new simultaneously valid for a short overlap window.
  2. Update the variable in Launchverse for production. The container restarts and picks up the new secret.
  3. Verify the new key is in use (hit a code path that uses it; check provider audit logs).
  4. Revoke the old key in the provider once you're confident.

For very long-lived keys with no rotation support (some legacy webhooks), the rotation strategy is "schedule downtime, swap, redeploy." Most secrets in 2026 support graceful overlap.

A reasonable rotation cadence:

Secret typeRotation cadence
Database passwordEvery 6–12 months, or after a known leak
Third-party API keysEvery 6 months
OAuth client secretsEvery 12 months
Internal service-to-service tokensEvery 3–6 months
GitHub deploy keys / app tokensEvery 3 months, or after a developer leaves

Don't do this

A short list of mistakes that ship to production every month:

  • Committing .env to git. Even private repos are not secure storage. Scan your history with git-secrets or truffleHog.
  • Logging secrets. Redact request headers, body, and query strings before they reach your log aggregator. Most logging libraries have a redact list — populate it.
  • Storing credentials in code. "I'll move it to env vars later" never happens. Use env vars from day one.
  • Storing secrets in client-side code. If the variable starts with NEXT_PUBLIC_, VITE_, or REACT_APP_, it's public. There's no way to make it private at runtime.
  • Sharing prod credentials in Slack DMs. Use 1Password, Bitwarden, or your team's secret manager. Slack history is searchable.

Further reading


Ready to deploy?

Start free in Naira — no card required, no FX surprises.

Have feedback or a topic to suggest? Talk to us.