Environment variables are how applications find their secrets, their database URLs, and their feature flags. Naming them well makes deployments predictable; naming them poorly turns every new environment into a small archeological dig.
The conventions for environment variable naming are mostly settled. Here's what every developer should know.
The case convention: CONSTANT_CASE (SCREAMING_SNAKE_CASE)
Environment variables are conventionally uppercase with underscores between words. DATABASE_URL, API_KEY, NODE_ENV, MAX_RETRY_COUNT.
This convention dates back to early Unix (the 1970s). The printenv output looks like:
PATH=/usr/local/bin:/usr/bin:/bin
HOME=/home/user
LANG=en_US.UTF-8
TERM=xterm-256color
Every system-set environment variable on every Unix-like operating system follows this pattern. Application-set variables follow it for consistency.
Why uppercase rather than lowercase? Several reasons:
- Distinguishes variables from commands in shell scripts.
echo $PATHreads as "this is a variable" because of the uppercase. - Matches the C macro convention from which much of Unix culture derives
- Reduces collisions with shell functions and aliases, which are conventionally lowercase
- Easier to grep for in code (uppercase identifiers stand out)
Naming patterns
Prefix by service or category
When you have many variables, prefix them by the service or category they belong to:
# Database
DATABASE_URL=postgres://...
DATABASE_POOL_SIZE=10
DATABASE_MAX_CONNECTIONS=100
# Redis
REDIS_URL=redis://...
REDIS_TTL_SECONDS=3600
# Stripe
STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
# Application
APP_NAME=myapp
APP_PORT=3000
APP_ENV=production
This makes related variables sort together alphabetically and easy to identify by scanning.
Common standard prefixes
Several prefixes have become near-universal:
NODE_*— Node.js runtime (NODE_ENV,NODE_OPTIONS)NPM_*— npm CLI (NPM_TOKEN,NPM_REGISTRY)AWS_*— AWS SDK and CLI (AWS_ACCESS_KEY_ID,AWS_REGION)DATABASE_*— generic database connection (DATABASE_URL)REDIS_*— Redis connection (REDIS_URL)NEXT_PUBLIC_*— Next.js client-exposed variablesREACT_APP_*— Create-React-App client-exposed variablesVITE_*— Vite client-exposed variables
If your variable would conflict with one of these, rename. Don't override AWS SDK variables for non-AWS purposes.
Standard environment variables
Several environment variables have settled into universal use across languages and frameworks:
| Variable | Purpose |
|---|---|
| NODE_ENV | Node runtime mode: development, production, test |
| PORT | HTTP port to listen on |
| DATABASE_URL | Full database connection string |
| REDIS_URL | Redis connection string |
| SECRET_KEY_BASE | Application secret for signing tokens, cookies |
| LOG_LEVEL | Logging verbosity: debug, info, warn, error |
| PATH | Shell binary search path (Unix) |
| HOME | User home directory (Unix) |
Use the standard name when one exists. DATABASE_URL rather than DB_CONNECTION_STRING or POSTGRES_URI — every modern framework and ORM looks for DATABASE_URL first.
The 12-factor app convention
The 12-Factor App manifesto codified a set of conventions for cloud-native applications, including how configuration should be handled. The core rules:
- Store config in environment variables, not in files committed to source control
- Strict separation of code and config — the same code should run in dev, staging, production with only env-var differences
- No grouping environment variables by environment: there shouldn't be a "production" group and a "development" group inside a single config file. Each environment has its own complete set.
This makes environment variable naming critical. Variables need to be self-describing because the values change per environment but the names stay the same.
Public vs. private variables
Modern web frameworks split variables into "public" (exposed to the browser) and "private" (server-only). The naming convention makes this distinction explicit:
- Next.js:
NEXT_PUBLIC_*variables get bundled into the client JavaScript. Everything else stays server-only. - Vite:
VITE_*variables are exposed to the client. - Create React App:
REACT_APP_*variables are exposed. - Astro:
PUBLIC_*variables are exposed.
The prefix makes the security boundary visible. A variable named NEXT_PUBLIC_API_URL can safely live in client code; a variable named STRIPE_SECRET_KEY can't, and if you accidentally tried to use it in a client component, the absence of the public prefix would alert you (or the build).
Boolean variables
Environment variables are always strings. There's no native boolean type. Conventions for boolean values:
- Truthy values:
1,true,yes,on - Falsy values:
0,false,no,off
The application is responsible for parsing the string. Most frameworks treat unset as falsy and any value other than the falsy ones as truthy. DEBUG=anything is truthy by default.
Naming-wise, treat booleans like other variables: ENABLE_DEBUG, FEATURE_NEW_DASHBOARD, USE_REDIS_CACHE. The verb prefix makes the boolean nature clearer.
Multi-value variables
Sometimes you need a list. Two conventions:
- Comma-separated:
ALLOWED_HOSTS=example.com,www.example.com,api.example.com - Space-separated:
ALLOWED_HOSTS=example.com www.example.com api.example.com(less common because shells split on space)
Comma-separated is safer. The application parses on commas and trims whitespace.
For complex structured config, use JSON in a single variable:
FEATURE_FLAGS={"new_dashboard":true,"beta_search":false}
This works but is harder to read. Prefer flat variables when possible (ENABLE_NEW_DASHBOARD, ENABLE_BETA_SEARCH).
Secrets
Secrets (API keys, database passwords, JWT signing keys) follow the same naming conventions as other env vars, but they need additional handling:
- Never commit secrets to source control. Use
.envfiles locally (gitignored) and a secrets manager in production. - Use
_KEY,_SECRET, or_TOKENsuffixes:STRIPE_SECRET_KEY,JWT_SECRET,GITHUB_TOKEN. The suffix signals "this is sensitive." - Don't log them. Many logging frameworks have allowlists/denylists for sensitive variable names. The convention helps.
Some teams prefix secrets explicitly: SECRET_STRIPE_KEY, SECRET_DATABASE_PASSWORD. This is fine if your secrets manager benefits from the consistent prefix.
Naming anti-patterns
Lowercase variable names: database_url rather than DATABASE_URL. Technically works but breaks convention. Some shell tools assume uppercase.
Hyphens: DATABASE-URL. Doesn't work in most shells because - is interpreted as an arithmetic operator.
Spaces or special characters: DATABASE URL, DATABASE@URL. Not allowed in any shell.
Numbers at the start: 3RD_PARTY_API_KEY. Not allowed in many shells (variables must start with a letter or underscore).
Names too generic: URL, KEY, SECRET. These collide with other tools. Always prefix.
Names tied to specific environments: PRODUCTION_DATABASE_URL, STAGING_API_KEY. Don't bake the environment into the variable name. The same variable name applies in all environments; the value differs.
The .env file convention
Most modern frameworks read environment variables from a .env file in the project root during development. The file format is unsurprising:
# .env
DATABASE_URL=postgres://localhost:5432/myapp_dev
REDIS_URL=redis://localhost:6379
STRIPE_SECRET_KEY=sk_test_...
LOG_LEVEL=debug
# A comment
APP_NAME=myapp
Conventions for .env files:
- One variable per line
NAME=valuewith no spaces around the equals sign- Comments start with
# - Values with special characters may need quotes:
SECRET_KEY="abc def" - The file is gitignored
- A
.env.examplefile (committed) shows the variable names with placeholder values
Converting from existing naming
If you inherit a codebase with non-conventional env variable names (camelCase, mixed case, etc.), our bulk converter can normalize a list to CONSTANT_CASE. Paste the variable names, switch to CONSTANT_CASE mode, and use the output to update both your .env file and the code that reads it.
For single variables, the CONSTANT_CASE converter handles one at a time.
The bigger principle
Environment variables are infrastructure. They're set by deployment systems, read by application code, and observed by operators debugging incidents. Names are the API between these audiences.
The conventions are conservative because the failure mode of bad naming is severe — a misnamed secret can leak, a misnamed connection string can connect to production by accident, a missing variable can crash the deployment. The discipline of uppercase, descriptive, well-prefixed names pays off every time someone new touches the infrastructure.