Multiple environments

Set up development, staging, and production environment variables for the web app.

For the complete documentation index, see llms.txt. Prefer markdown by appending .md to documentation URLs or sending Accept: text/markdown.

Use separate environments when you want local development, staging, and production to talk to different databases, auth callbacks, payment accounts, analytics projects, or feature flags.

The safe pattern is:

  • keep variable names consistent across environments
  • keep secrets out of Git
  • expose only browser-safe values with NEXT_PUBLIC_
  • use APP_ENV for your own environment name instead of changing NODE_ENV

Good to know

Next.js treats NEXT_PUBLIC_ variables as client-side values. Anything with that prefix is bundled into the browser build, so never use it for database URLs, API secrets, webhook secrets, or private keys.

Choose your environments

Most apps only need three:

  • development - local work with local or sandbox services
  • staging - production-like testing before release
  • production - the live customer environment

Use the same variable names in every environment. Only the values should change.

.env.example
APP_ENV="development"
URL="http://localhost:3000"
DATABASE_URL=""
BETTER_AUTH_SECRET=""
BETTER_AUTH_URL="${URL}"
NEXT_PUBLIC_URL="${URL}"
NEXT_PUBLIC_APP_ENV="${APP_ENV}"

Create local env files

For day-to-day development, keep local values in ignored .env.local files:

.env.local
APP_ENV="development"
URL="http://localhost:3000"
DATABASE_URL="postgresql://user:password@localhost:5432/app"
BETTER_AUTH_SECRET="local-secret"
BETTER_AUTH_URL="${URL}"
apps/web/.env.local
NEXT_PUBLIC_URL="${URL}"
NEXT_PUBLIC_APP_ENV="${APP_ENV}"
NEXT_PUBLIC_THEME_MODE="system"
NEXT_PUBLIC_THEME_COLOR="orange"

The root file is a good place for shared server-side values. The app file is a good place for web-only values.

Add staging and production values

For local staging checks, create environment-specific local files:

.env.staging.local
APP_ENV="staging"
URL="https://staging.example.com"
DATABASE_URL="postgresql://..."
BETTER_AUTH_SECRET="..."
BETTER_AUTH_URL="${URL}"
apps/web/.env.staging.local
NEXT_PUBLIC_URL="${URL}"
NEXT_PUBLIC_APP_ENV="${APP_ENV}"
NEXT_PUBLIC_THEME_MODE="system"
NEXT_PUBLIC_THEME_COLOR="orange"

Repeat the same shape for production:

.env.production.local
APP_ENV="production"
URL="https://example.com"
DATABASE_URL="postgresql://..."
BETTER_AUTH_SECRET="..."
BETTER_AUTH_URL="${URL}"

Do not commit secrets

Commit .env.example with empty or safe placeholder values. Keep .env.local, .env.staging.local, and .env.production.local ignored.

Add environment-aware scripts

The project already uses dotenv-cli, so you can load an environment by name with dotenv -c <environment>.

package.json
{
  "scripts": {
    "dev:web": "dotenv -c development -- turbo dev --filter=web",
    "dev:web:staging": "dotenv -c staging -- turbo dev --filter=web",
    "build:web:staging": "dotenv -c staging -- turbo build --filter=web",
    "build:web:production": "dotenv -c production -- turbo build --filter=web"
  }
}

Then run:

pnpm dev:web:staging
pnpm build:web:production

This loads .env, .env.<environment>, .env.local, and .env.<environment>.local in cascade order.

Configure your hosting provider

In your hosting provider, create separate environment groups or projects for staging and production.

Set the same variables there:

Hosting provider variables
APP_ENV="production"
URL="https://example.com"
DATABASE_URL="postgresql://..."
BETTER_AUTH_SECRET="..."
BETTER_AUTH_URL="https://example.com"
NEXT_PUBLIC_URL="https://example.com"
NEXT_PUBLIC_APP_ENV="production"

Before going live, verify:

  • auth callback URLs point to the same environment
  • webhooks point to the same environment
  • payment providers use test keys in staging and live keys in production
  • analytics and monitoring projects are separated or tagged by APP_ENV
  • NEXT_PUBLIC_ values contain no secrets

Useful references

How is this guide?

Last updated on

On this page

Ship your startup everywhere. In minutes.Try TurboStarter