How to Manage Environment Variables for Different Deployment Environments in Next.js

Use Next.js's built-in @next/env loader to create mode-specific .env files (such as .env.production, .env.staging, and .env.development) that automatically load based on the current build phase without requiring custom configuration or third-party libraries.

Managing environment variables across multiple deployment environments—including development, staging, and production—requires a robust strategy beyond the standard .env.local file. Next.js provides a powerful, built-in solution through the @next/env package, which automatically loads environment files based on the current build mode. This approach eliminates the need for custom loading scripts while maintaining clean separation between environment-specific configurations.

Understanding Next.js Environment File Loading Order

The core mechanism for environment variable management resides in the @next/env package. When the Next.js server initializes or the build process starts, it invokes loadEnvConfig(dir, dev, log) from packages/next-env/index.ts. This function is called during server startup in packages/next/src/server/config.ts (around line 1580) and determines which files to load based on the current phase.

The Four-Layer Precedence System

The loadEnvConfig function constructs an ordered list of files, loading them sequentially with later files overriding earlier ones. According to the implementation in packages/next-env/index.ts (lines 38-46), the loading order is:

  1. .env.{mode}.local — Environment-specific local overrides (e.g., .env.production.local). Loaded first but overridden by later files in the chain.
  2. .env.local — General local overrides. Skipped entirely when mode is test to ensure test determinism.
  3. .env.{mode} — Environment-specific defaults (e.g., .env.staging, .env.development).
  4. .env — Global defaults loaded last, providing base values for all environments.

The mode is derived from the phase Next.js runs in: development (during next dev), production (during next build with NODE_ENV=production), or test (during testing). You can also specify custom modes by setting the NODE_ENV environment variable.

How Build Modes Determine File Selection

When you execute next build, Next.js checks the current phase and passes this information to loadEnvConfig. In packages/next/src/server/config.ts, the configuration loader determines whether the current context is development, production, or a custom mode, then loads the corresponding environment files.

For example, running NODE_ENV=staging next build triggers the loading of .env.staging.local, .env.local, .env.staging, and .env in that order. This allows you to maintain separate configurations for preview deployments, staging servers, and production environments without modifying your application code.

Setting Up Environment Files for Multiple Deployment Targets

To effectively manage environment variables across different deployment environments, create mode-specific files in your project root alongside the standard .env.local file.

Standard Environment Configuration

For the three primary environments supported by Next.js, create the following files:

  • .env.development — Default variables for local development (e.g., API endpoints pointing to localhost).
  • .env.production — Default variables for production builds (e.g., production API endpoints).
  • .env.local — Local overrides that should never be committed (contains secrets, personal API keys).

When running next dev, Next.js automatically loads .env.development.local, .env.local, .env.development, and .env.

Creating Custom Environment Modes (Staging)

For environments like staging or qa that fall between development and production, leverage custom modes by setting NODE_ENV:

// package.json
{
  "scripts": {
    "build:staging": "NODE_ENV=staging next build",
    "build:production": "next build"
  }
}

Create the corresponding files:

  • .env.staging — Staging-specific defaults (e.g., staging API URL).
  • .env.staging.local — Local staging overrides.

When you run npm run build:staging, Next.js loads the staging environment files in the standard precedence order, allowing you to deploy to preview environments with distinct configurations from production.

Accessing Environment Variables in Your Application

Once the environment files are loaded via loadEnvConfig, variables are available via process.env throughout your application.

Server-Side Access

In API routes, server components (App Router), or getServerSideProps, access variables directly:

// app/api/config/route.ts
export async function GET() {
  const apiKey = process.env.API_KEY;
  const environment = process.env.NODE_ENV;
  
  return Response.json({ environment, apiKey });
}

Client-Side Access

To expose variables to the browser, prefix them with NEXT_PUBLIC_:


# .env.production

NEXT_PUBLIC_API_URL=https://api.production.com
API_SECRET_KEY=server-only-secret
// components/ApiClient.tsx
export default function ApiClient() {
  // Available in browser
  const apiUrl = process.env.NEXT_PUBLIC_API_URL;
  
  return <div>Connecting to {apiUrl}</div>;
}

Integrating with CI/CD and Vercel

When deploying through CI/CD pipelines or platforms like Vercel, environment variable management extends beyond local files.

Vercel Deployment Considerations

Vercel automatically injects environment variables defined in the project dashboard during the build process. According to the Next.js source implementation in packages/next-env/index.ts, these platform-injected variables are merged into process.env after the .env files are loaded via loadEnvConfig. This means:

  • Vercel dashboard variables override values in .env.production
  • You can combine static files with Vercel's UI-defined secrets
  • Preview deployments automatically use the appropriate environment based on your Git branch settings

CI/CD Pipeline Configuration

In GitHub Actions, GitLab CI, or similar platforms, set environment variables before running the build:


# .github/workflows/deploy-staging.yml

jobs:
  build:
    runs-on: ubuntu-latest
    environment: staging
    env:
      NODE_ENV: staging
      API_KEY: ${{ secrets.STAGING_API_KEY }}
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run build

The NODE_ENV=staging setting ensures Next.js loads .env.staging files, while the API_KEY environment variable takes precedence over file-defined values.

Summary

  • Use mode-specific files: Create .env.development, .env.staging, and .env.production to separate configurations by deployment target.
  • Leverage @next/env: The framework automatically loads files via loadEnvConfig in packages/next-env/index.ts, following a strict precedence order.
  • Respect the loading order: Files load as .env.{mode}.local → .env.local → .env.{mode} → .env, with later values overriding earlier ones.
  • Combine with platform variables: Vercel and CI/CD injected variables merge after file loading, allowing hybrid approaches for secrets management.
  • Use custom modes: Set NODE_ENV to custom values like staging to trigger loading of .env.staging files during builds.

Frequently Asked Questions

How do I add a staging environment to Next.js?

Create a .env.staging file in your project root with staging-specific variables, then run your build with NODE_ENV=staging next build. Next.js will automatically load .env.staging.local, .env.local, .env.staging, and .env in that order. You can add a convenience script to your package.json such as "build:staging": "NODE_ENV=staging next build" to simplify the process.

Can I use multiple .env files in production?

Yes, Next.js supports multiple environment files in production builds. The framework loads .env.production.local, .env.local, .env.production, and .env when running next build with NODE_ENV=production. However, .env.production.local and .env.local should typically be excluded from version control via .gitignore, so production deployments often rely on .env.production committed to the repository combined with platform-injected variables from Vercel or CI/CD systems.

What takes precedence: Vercel environment variables or .env files?

Vercel environment variables take precedence over values defined in .env files. According to the Next.js source code in packages/next-env/index.ts, the loadEnvConfig function processes local files first, then Vercel injects its variables into process.env after the build starts. This means a variable defined in the Vercel dashboard will override the same variable defined in .env.production, allowing you to keep sensitive production secrets out of your repository while maintaining default configurations in files.

How does Next.js handle environment variables during next build?

During next build, Next.js calls loadEnvConfig from @next/env (implemented in packages/next-env/index.ts) to load environment files based on the current mode. The function constructs a list of files in specific precedence order: .env.{mode}.local, .env.local, .env.{mode}, and .env. It then parses each file and merges the variables into process.env, with later files overriding earlier ones. This happens before the build process begins, ensuring all environment variables are available during compilation, static generation, and server initialization.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →