Build-time vs runtime
| Variable kind | Read when? |
|---|---|
NEXT_PUBLIC_* | Build time — baked into the bundle |
| Server-only | Runtime — read on every request |
The Docker problem
Building one image and deploying it to staging + production with different settings? NEXT_PUBLIC_* values can't change between environments — they're already baked in. You either rebuild per environment or use runtime config strategies (e.g., proxy injects them, or you read from server only).
Loading order
- Already-set
process.env .env.production.local/.env.development.local.env.local(skipped in test).env.production/.env.development.env
Maintain a typed inventory of configuration with owner, secrecy, build-time or runtime binding, and required environments. Validate server variables on startup, expose public values intentionally, and rotate secrets without requiring source changes. After changing an environment value, request both new deployments and old instances to detect mixed state. If two values coexist during a rolling deploy, data formats and flags must tolerate that interval. Copying one .env file between environments collapses boundaries and invites production credentials into local tools. Conversely, building one image for promotion cannot support different NEXT_PUBLIC_ values after the client bundle already exists. Compare preview and production inventories without printing values, start with one required key missing, rotate a credential, and promote the same artifact where the contract allows it. Search logs, build output, source maps, and browser bundles for secret markers.