- Next.js
- Production Operations & DevOps
- Managing Environment Variables & Build-Time Inlining
Managing Environment Variables & Build-Time Inlining
Next.js differentiates between private server-side environment variables and public client-side variables prefixed with NEXT_PUBLIC_. Private variables are evaluated at runtime on the server, while NEXT_PUBLIC_ variables are statically inlined (hardcoded) into client JavaScript bundles during next build. Modifying NEXT_PUBLIC_ variables requires a complete rebuild.
Learning Objectives
Differentiate server-only variables from NEXT_PUBLIC_ client-inlined variables.
Avoid accidental production data leaks caused by variable prefix misconfigurations.
Validate environment variables at build/startup using @t3-oss/env-nextjs and Zod.
Handle runtime dynamic environment variables in Docker and Kubernetes.
Prerequisites & Background Knowledge
Why Interviewers Care
Frequent cause of broken deployments and security leaks in production environments.
The Challenge
Explain how Next.js treats private environment variables versus NEXT_PUBLIC_ variables. Implement type-safe environment variable validation using Zod that halts invalid builds immediately.
Code Architecture & Implementation
1// lib/env.ts2import { z } from 'zod';34// Schema validating environment variables at build and boot time5const EnvSchema = z.object({6 // Server-only secrets: MUST NOT start with NEXT_PUBLIC_7 DATABASE_URL: z.string().url('Invalid database connection URL'),8 JWT_SECRET: z.string().min(32, 'JWT secret must be at least 32 characters'),9 NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),1011 // Public client variables: MUST start with NEXT_PUBLIC_12 NEXT_PUBLIC_SITE_URL: z.string().url(),13 NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: z.string().startsWith('pk_'),14});1516// Parse process.env; throws descriptive error if variables are missing!17const parsedEnv = EnvSchema.safeParse(process.env);1819if (!parsedEnv.success) {20 console.error('❌ CRITICAL: Invalid environment variables:');21 console.error(JSON.stringify(parsedEnv.error.format(), null, 2));22 process.exit(1); // Fail the build immediately!23}2425export const env = parsedEnv.data;
Architectural Explanation
Because the browser has no operating system environment, client-accessible variables must be bundled into JavaScript files. Next.js searches for process.env.NEXT_PUBLIC_* references and replaces them with their actual string values during compilation. If you deploy a Docker image to production with NEXT_PUBLIC_API_URL set at container runtime, it will have NO effect because the value was already hardcoded during npm run build. Server-side variables without NEXT_PUBLIC_ are read dynamically from the host environment on every request.
Common Mistakes & Anti-Patterns
Prefixing sensitive API keys with `NEXT_PUBLIC_` (e.g. `NEXT_PUBLIC_STRIPE_SECRET_KEY`), publicly exposing secrets in the browser console.
Expecting `NEXT_PUBLIC_` variables to change dynamically at Docker container startup without rebuilding.
Not validating environment variables in CI/CD, allowing deployments with missing configuration to crash at runtime.
Interview Follow-Up Traps
The interviewer asks: "What happens if a Client Component attempts to read process.env.DATABASE_URL?" Next.js replaces it with `undefined` in client bundles to protect the secret, but a TypeScript warning or runtime bug will occur.
Frequently Asked Questions
How do you support runtime dynamic public config in Docker?
Pass runtime config via an un-cached `/api/config` endpoint or window.__RUNTIME_CONFIG__ script.
Should .env.local be committed to Git?
Never! `.env.local` should be in `.gitignore` to keep developer credentials secure.
