Overview
Vercel stores environment variables encrypted at rest and injects them at build time or at function runtime. Mis-scoped variables cause two recurring failures: preview deployments that touch production data, and builds that fail because a variable is missing. Give each variable only the environments that need it, use the Secret type for credentials, and redeploy after every change. General secret hygiene (git, scanning, client prefixes) is on secrets-and-env.
Give each variable only the environments that need it
Choose any of Production, Preview, Development, and custom environments per variable. Preview variables can also be limited to one Git branch, and a branch-specific value overrides the general Preview one.
| Variable | Development | Preview | Production |
|---|---|---|---|
DATABASE_URL | local | staging | production |
STRIPE_SECRET_KEY | test key | test key | live key |
NEXT_PUBLIC_API_URL | http://localhost:3001 | preview API | production API |
Never give Preview the production DATABASE_URL: a bad migration or test write from an unmerged branch would hit production (vercel-preview-deployments).
Use the Secret type for credentials
Since August 2026 variables have two types: Config (value stays readable to project members) and Secret (the value can be replaced but never viewed or retrieved). Variables previously marked Sensitive became Secrets with no migration. On the CLI, --visibility secret or --visibility config sets the type, and the legacy --sensitive and --no-sensitive flags still map to Secret and Config (changelog). A team policy, “Separate Production Secret Values”, requires Production secrets to differ from other environments; the older “Enforce Sensitive Environment Variables” policy is deprecated.
Make these Secrets: API keys, database URLs with credentials, JWT signing secrets, OAuth client secrets. Public values (API base URL, analytics ID) can stay Config. Secret type limits viewing; it does not replace access control on who can edit variables.
Redeploy after every change
Changes apply only to new deployments; existing ones keep the old values. After editing a variable, trigger a new deployment before assuming it took effect.
Keep NEXT_PUBLIC_ values free of secrets
Next.js inlines NEXT_PUBLIC_* variables into the browser bundle at build time, so any value with that prefix is public. Variables without it are available only in server code. Details: nextjs and secrets-and-env.
Stay under the size limits
Variables on Node.js, Python, Ruby, Go, and the PHP community runtime may total 64 KB per deployment (no single variable above 64 KB). Edge runtime functions and Middleware with the edge runtime cap each variable at 5 KB (docs). Store large certificates or JWKS as files fetched at runtime if either limit is a problem.
Pull and add variables with the CLI
vercel env pull .env.local # writes Development-scoped values
vercel env add DATABASE_URL production --visibility secret
vercel env add STRIPE_TEST_KEY preview development
vercel env ls # names, not valuesAdd .env.local to .gitignore and re-pull when a variable changes; local drift from the Development scope causes “works locally, fails on preview”. If you manage variables with Terraform, remember values land in Terraform state, which must itself be encrypted and access-controlled.
Rotate secrets with overlapping credentials
- Create the new credential upstream.
- Update the Vercel variable.
- Deploy and confirm the new deployment works.
- Revoke the old credential.
Deployments are immutable, so the live deployment keeps the old value until the new one is promoted. Revoking first causes an outage if the deploy is slow.