Vercel
beginner
15-20 minutes

Environment Variables

Manage secrets and configuration across development, preview, and production environments on Vercel.

What you'll accomplish

Environment variables configured for all deployment environments

Prerequisites

  • Project deployed to Vercel

Understanding Vercel Environments

Vercel has three deployment environments:

EnvironmentWhen UsedURL Pattern
Productionmain branch deploysyour-app.vercel.app
PreviewOther branches, PRsyour-app-git-branch.vercel.app
DevelopmentLocal with vercel devlocalhost:3000

Each can have different environment variable values.

┌─────────────────────────────────────────────────────────┐
│                    Your Project                         │
├─────────────┬─────────────────┬───────────────────────┤
│ Development │     Preview     │     Production        │
├─────────────┼─────────────────┼───────────────────────┤
│ DB: local   │ DB: staging     │ DB: production        │
│ API: mock   │ API: staging    │ API: production       │
│ DEBUG: true │ DEBUG: true     │ DEBUG: false          │
└─────────────┴─────────────────┴───────────────────────┘

Adding Environment Variables

Via Dashboard (Recommended for Secrets)

  1. Go to your project in Vercel Dashboard
  2. Navigate to Settings → Environment Variables
  3. Click Add New
  4. Enter:
    • Name: Variable name (e.g., DATABASE_URL)
    • Value: The secret value
    • Environments: Select which environments need this value

Via CLI

bash
# Add interactively
vercel env add DATABASE_URL

# Prompts:
# ? Which Environments? Production, Preview, Development
# ? What's the value? postgresql://user:pass@host/db

# Add to specific environment
vercel env add API_KEY production

Via vercel.json (Non-Sensitive Only)

For values that aren't secrets:

json
{
  "env": {
    "NEXT_PUBLIC_SITE_URL": "https://example.com",
    "NODE_ENV": "production"
  }
}
Note

Never put secrets in vercel.json - it's committed to Git! Use the dashboard or CLI for sensitive values.

Variable Naming

Client-Side vs Server-Side

For Next.js, variable names control where they're available:

PrefixAvailable InExample
NEXT_PUBLIC_Browser + ServerNEXT_PUBLIC_API_URL
(none)Server onlyDATABASE_URL
javascript
// Server-side (API routes, getServerSideProps)
process.env.DATABASE_URL  // ✓ Works

// Client-side (React components)
process.env.NEXT_PUBLIC_API_URL  // ✓ Works
process.env.DATABASE_URL  // ✗ undefined
Note

Never prefix secrets with NEXT_PUBLIC_ - they'll be exposed in client-side JavaScript bundles!

Other Frameworks

FrameworkClient Prefix
ViteVITE_
Create React AppREACT_APP_
Vue CLIVUE_APP_
NuxtNUXT_PUBLIC_
SvelteKitPUBLIC_

Environment-Specific Values

Same Variable, Different Values

You can set different values per environment:

DATABASE_URL:
├── Production:  postgresql://prod-db.example.com/app
├── Preview:     postgresql://staging-db.example.com/app
└── Development: postgresql://localhost/app_dev

Example Setup

Production variables:

DATABASE_URL=postgresql://prod-db.example.com/myapp
STRIPE_SECRET_KEY=sk_live_xxxxxxxx
NEXT_PUBLIC_API_URL=https://api.example.com

Preview variables:

DATABASE_URL=postgresql://staging-db.example.com/myapp
STRIPE_SECRET_KEY=sk_test_xxxxxxxx
NEXT_PUBLIC_API_URL=https://staging-api.example.com

Development variables:

DATABASE_URL=postgresql://localhost/myapp_dev
STRIPE_SECRET_KEY=sk_test_xxxxxxxx
NEXT_PUBLIC_API_URL=http://localhost:3001

Pulling Variables Locally

Pull to .env.local

bash
# Pull all variables for development
vercel env pull

# Creates .env.local:
# DATABASE_URL="postgresql://localhost/myapp_dev"
# STRIPE_SECRET_KEY="sk_test_xxxxxxxx"
# NEXT_PUBLIC_API_URL="http://localhost:3001"

Pull Specific Environment

bash
# Pull production values
vercel env pull --environment=production .env.production.local

# Pull preview values
vercel env pull --environment=preview .env.preview.local

.gitignore

Make sure to ignore local env files:

gitignore
# .gitignore
.env.local
.env.development.local
.env.production.local
.env.preview.local

System Environment Variables

Vercel provides these automatically:

VariableDescription
VERCEL"1" when running on Vercel
VERCEL_ENVproduction, preview, or development
VERCEL_URLDeployment URL (without protocol)
VERCEL_BRANCH_URLBranch-specific URL
VERCEL_GIT_COMMIT_SHAGit commit hash
VERCEL_GIT_COMMIT_MESSAGECommit message
VERCEL_GIT_COMMIT_AUTHOR_NAMECommit author
VERCEL_GIT_REPO_SLUGRepository name
VERCEL_GIT_REPO_OWNERRepository owner
VERCEL_GIT_PROVIDERgithub, gitlab, or bitbucket

Using System Variables

javascript
// Detect if running on Vercel
if (process.env.VERCEL) {
  console.log('Running on Vercel');
}

// Get current environment
const isProduction = process.env.VERCEL_ENV === 'production';

// Construct URL
const url = process.env.VERCEL_URL
  ? `https://${process.env.VERCEL_URL}`
  : 'http://localhost:3000';

Sensitive vs Plain Text

Sensitive (Encrypted)

Choose "Sensitive" for:

  • API keys
  • Database passwords
  • Authentication secrets
  • Private keys

Sensitive values are:

  • Encrypted at rest
  • Hidden in UI after creation
  • Can only be viewed via CLI or recreated

Plain Text

Choose "Plain" for:

  • Public URLs
  • Feature flags
  • Configuration values
  • Non-secret settings

Referencing Other Variables

You can reference other variables:

bash
# In dashboard or CLI, use ${}
BASE_URL=https://api.example.com
API_ENDPOINT=${BASE_URL}/v1

# Result: API_ENDPOINT = "https://api.example.com/v1"

Build vs Runtime

Build-Time Variables

Available during npm run build:

  • Used for static generation
  • Baked into the build
  • Changing requires rebuild

Runtime Variables

Available when functions execute:

  • Fresh on each request
  • Can be updated without rebuild
  • Only for server-side code
Note

All variables are available at build time. But NEXT_PUBLIC_ variables are baked in - you can't change them without rebuilding.

Managing Variables

List All Variables

bash
vercel env ls

# Output:
# ┌──────────────────────┬────────────────┬─────────────────────┐
# │ Name                 │ Value          │ Environments        │
# ├──────────────────────┼────────────────┼─────────────────────┤
# │ DATABASE_URL         │ ●●●●●●●●●●●    │ All                 │
# │ NEXT_PUBLIC_API_URL  │ https://...    │ Production          │
# │ DEBUG                │ true           │ Preview, Dev        │
# └──────────────────────┴────────────────┴─────────────────────┘

Remove Variable

bash
# Remove from all environments
vercel env rm DATABASE_URL

# Remove from specific environment
vercel env rm DATABASE_URL production

Troubleshooting

Variable Not Available

  1. Check environment selection - Is it set for Production/Preview/Development?
  2. Check the prefix - Client-side needs NEXT_PUBLIC_
  3. Redeploy - Changes require a new deployment
  4. Check spelling - Variable names are case-sensitive

Debugging

javascript
// Log all available env vars (development only!)
console.log('Environment:', process.env.VERCEL_ENV);
console.log('Available vars:', Object.keys(process.env).filter(k => k.startsWith('NEXT_PUBLIC_')));

Common Mistakes

ProblemSolution
Variable undefined in browserAdd NEXT_PUBLIC_ prefix
Secret showing in clientRemove NEXT_PUBLIC_ prefix
Different value than expectedCheck environment (prod vs preview)
Changes not applyingTrigger new deployment

Security Best Practices

  1. Never log secrets - Even in development
  2. Rotate regularly - Especially API keys
  3. Minimal access - Only add to needed environments
  4. Use Sensitive type - For anything confidential
  5. Review periodically - Remove unused variables
Note

Checkpoint: Set up environment variables for Production and Preview environments, then run vercel env pull to get them locally.