env
Manage Vercel environment variables. Commands include list, pull, add, remove, and diff. Use to sync environment variables between Vercel and your local development environment.
SKILL.mdvercel-dev/skills/env/SKILL.md
GitHubVercel Environment Variables
Manage environment variables for the current Vercel project with safety rails to prevent secret leakage.
🔒 Never-Echo-Secrets Rule: Environment variable values must never appear in command output, summaries, or conversation text. Only show variable names, environments, and metadata (created date, type). This rule applies to all subcommands.
Preflight
- CLI available? — Confirm
vercelis on PATH.- If missing:
npm i -g vercel(orpnpm add -g vercel/bun add -g vercel).
- If missing:
- Project linked? — Check for
.vercel/project.jsonin the current directory or nearest parent.- If not found: run
vercel linkinteractively, then re-run/env.
- If not found: run
- Detect environment files — Check for
.env,.env.local,.env.production.local,.env.development.localin the project root. Note which exist for the diff subcommand.
Plan
Based on "$ARGUMENTS", determine the action:
| Argument | Action | Destructive? |
|---|---|---|
list / ls / (none) |
List env var names per environment | No |
pull |
Download env vars to local .env.local |
No (overwrites local file) |
add <NAME> |
Add a new env var | Yes (if production) |
rm <NAME> / remove <NAME> |
Remove an env var | Yes |
diff |
Compare local vs Vercel key names | No |
For any operation that mutates production environment variables (add or rm targeting production):
⚠️ Production environment mutation requested. This will change environment variables on your live production deployment. Ask the user for explicit confirmation before proceeding. Do not mutate production env vars without a clear "yes."
Commands
"list" or "ls" or no arguments
<!-- Sourced from env-vars skill: vercel env CLI > List Environment Variables --># List all environment variables
vercel env ls
# Filter by environment
vercel env ls production
Present results as a table of variable names only grouped by environment. Never print values.
| Name | Production | Preview | Development |
|---|---|---|---|
| DATABASE_URL | ✓ | ✓ | ✓ |
| API_KEY | ✓ | ✓ | — |
"pull"
<!-- Sourced from env-vars skill: vercel env CLI > Pull Environment Variables --># Pull all env vars for the current environment into .env.local
vercel env pull .env.local
# Pull for a specific environment
vercel env pull .env.local --environment=production
vercel env pull .env.local --environment=preview
vercel env pull .env.local --environment=development
# Overwrite existing file without prompting
vercel env pull .env.local --yes
# Pull to a custom file
vercel env pull .env.production.local --environment=production
After pulling, remind the user:
Ensure
.env*.localis in your.gitignoreto avoid committing secrets.
"add <NAME>"
- Ask the user which environments to target: production, preview, development (can select multiple).
- If production is selected, require explicit confirmation (see Plan section).
- Run the add command:
Every variable has a type:
| Type | After saving | Use for |
|---|---|---|
| Secret | Hidden in the dashboard and vercel env ls; can be replaced, never read back. Production and Preview Secrets are not returned by vercel env pull. |
Passwords, API keys, tokens, database URLs |
| Config | Readable by members with access | Non-sensitive values you need to read later |
Deployments receive both types at build time and runtime. Secret and Config replaced the Sensitive toggle; existing Sensitive variables are Secrets.
# Interactive — prompts for value, environments, and type
vercel env add MY_SECRET
# Non-interactive: read the value from a file so it never lands in shell
# history or process arguments (echo "value" | ... and --value do both)
vercel env add MY_SECRET production < ./secret.txt
# Add to production and preview in one command
vercel env add MY_SECRET production,preview < ./secret.txt
# Set the type explicitly
vercel env add MY_SECRET production --type secret < ./secret.txt
vercel env add SITE_REGION production --type config < ./region.txt
# Add development in its own command; development-only adds default to Config
vercel env add MY_SECRET development < ./dev-secret.txt
# Update an existing value
vercel env update MY_SECRET production < ./secret.txt
- Defaults: a non-interactive add to production, preview, or a custom environment is stored as Secret.
- Public prefixes are always Config: variables such as
NEXT_PUBLIC_*orVITE_*are exposed to browsers, so the CLI refuses--type secretfor them. Keep a private value under a name without the prefix. - Flags:
--type config|secretneeds Vercel CLI 59.6 or later. Older CLIs use--visibility, now a deprecated alias of--type.--sensitive(Secret) and--no-sensitive(Config) still work in every version. - Team policy: Separate Production Secret Values requires a Production Secret to differ from the Preview, Development, and custom environment values of the same key. Under it, create separate Production and non-Production values instead of adding one value to all targets. It replaces the deprecated Enforce Sensitive Environment Variables policy.
The CLI will prompt for the value interactively — do not pass the value as a CLI argument or echo it.
"rm <NAME>" or "remove <NAME>"
- If the variable exists in production, require explicit confirmation.
- Run the remove command:
# Remove from specific environment
vercel env rm MY_SECRET production
# Remove from all environments
vercel env rm MY_SECRET
Confirm the target environment(s) with the user before executing.
"diff"
Compare local environment file key names against Vercel-configured key names. Only compare names — never read or display values.
- Read local env file keys (from
.env.localor.env— whichever exists):
grep -v '^#' .env.local | grep '=' | cut -d'=' -f1 | sort
- Fetch Vercel env var names:
vercel env ls
Parse the output to extract variable names for the target environment (default: development).
- Present the diff:
## Env Diff — Local vs Vercel (development)
### In local but NOT on Vercel:
- EXTRA_LOCAL_VAR
- DEBUG_MODE
### On Vercel but NOT in local:
- ANALYTICS_KEY
- SENTRY_DSN
### In both:
- DATABASE_URL
- API_KEY
- NEXT_PUBLIC_APP_URL
If all keys match, report: "Local and Vercel environment keys are in sync."
Environment-Specific Configuration Reference
<!-- Sourced from env-vars skill: Environment-Specific Configuration -->Vercel Dashboard vs .env Files
| Use Case | Where to Set |
|---|---|
| Secrets (API keys, tokens) | Vercel Dashboard (https://vercel.com/{team}/{project}/settings/environment-variables) or vercel env add, as type Secret |
| Public config (site URL, feature flags) | .env or .env.[environment] files |
| Local-only overrides | .env.local |
| CI/CD secrets | Vercel Dashboard (https://vercel.com/{team}/{project}/settings/environment-variables) with environment scoping |
Environment Scoping on Vercel
Variables set in the Vercel Dashboard at https://vercel.com/{team}/{project}/settings/environment-variables can be scoped to:
- Production — production domain deployments
- Preview — branch/PR deployments
- Development —
vercel devandvercel env pull
A variable can be assigned to one, two, or all three environments.
Git Branch Overrides
Preview environment variables can be scoped to specific Git branches:
# Add a variable only for the "staging" branch
vercel env add DATABASE_URL preview --git-branch=staging < ./staging-database-url.txt
Common Gotchas
<!-- Sourced from env-vars skill: Gotchas -->vercel env pull Overwrites Custom Variables
vercel env pull .env.local replaces the entire file — any manually added variables (custom secrets, local overrides, debug flags) are lost. Always back up or re-add custom vars after pulling:
# Save custom vars before pulling
grep -v '^#' .env.local | grep -v '^VERCEL_\|^POSTGRES_\|^NEXT_PUBLIC_' > .env.custom.bak
vercel env pull .env.local --yes
cat .env.custom.bak >> .env.local # Re-append custom vars
Or maintain custom vars in a separate .env.development.local file (loaded after .env.local by Next.js).
Pulled Files Omit Production and Preview Secrets
vercel env pull --environment=production (or preview) does not write Secret values, so a pulled file cannot reproduce production credentials. Keep separate Development values for local work instead of trying to copy production Secrets onto a machine.
Scripts Don't Auto-Load .env.local
Only Next.js auto-loads .env.local. Standalone scripts (drizzle-kit, tsx, custom Node scripts) need explicit loading:
# Use dotenv-cli
npm install -D dotenv-cli
npx dotenv -e .env.local -- npx drizzle-kit push
npx dotenv -e .env.local -- npx tsx scripts/seed.ts
# Or source manually
source <(grep -v '^#' .env.local | sed 's/^/export /') && node scripts/migrate.js
Verification
After any mutation (add or rm), verify the change took effect:
vercel env ls
Re-list environment variables and confirm:
- For
add: the new variable name appears in the expected environment(s). - For
rm: the variable name no longer appears in the target environment(s).
If verification fails (variable still present after remove, or missing after add), report the discrepancy and suggest retrying.
Summary
Present a structured result block:
## Env Result
- **Action**: list | pull | add | remove | diff
- **Status**: success | failed
- **Variable**: <NAME> (for add/remove)
- **Environments**: production, preview, development (as applicable)
- **Details**: <key outcome>
For diff, include counts:
- **Local only**: N keys
- **Vercel only**: N keys
- **Shared**: N keys
Next Steps
Based on the action performed:
- After list → "Run
/env pullto sync to local, or/env diffto compare local vs Vercel." - After pull → "Restart your dev server to pick up the new variables. Run
/env diffto verify sync." - After add → "Run
/deployto make the new variable available in your next deployment. For production, the variable is available immediately on the next request." - After remove → "The variable is removed. If your app depends on it, update your code or add a replacement. Consider redeploying with
/deploy." - After diff → "Add missing variables with
/env add <NAME>, or pull from Vercel with/env pullto sync."