Environment variables are key-value pairs that live outside your code. They store configuration that changes between environments β API keys, database URLs, feature flags β without hardcoding them.
# Environment variable
DATABASE_URL=postgres://user:pass@localhost:5432/mydb
Your code reads this value at runtime instead of having the connection string baked in.
Why use them
Security. API keys and passwords donβt belong in your code. If your code is on GitHub, anyone can see hardcoded secrets.
Flexibility. Same code, different config per environment:
| Variable | Development | Production |
|---|---|---|
DATABASE_URL | localhost:5432/dev | rds.amazonaws.com/prod |
API_KEY | test_key_123 | live_key_abc |
DEBUG | true | false |
The .env file
Most projects use a .env file in the project root:
# .env
DATABASE_URL=postgres://user:pass@localhost:5432/mydb
API_KEY=sk-test-abc123
NODE_ENV=development
Load it with dotenv (Node.js):
import 'dotenv/config';
console.log(process.env.DATABASE_URL);
Python:
from dotenv import load_dotenv
import os
load_dotenv()
print(os.environ["DATABASE_URL"])
Critical rule: never commit .env
Add .env to .gitignore:
# .gitignore
.env
.env.local
.env.production
Instead, commit a .env.example with placeholder values so other developers know which variables are needed:
# .env.example
DATABASE_URL=
API_KEY=
NODE_ENV=development
Framework-specific patterns
Next.js / Vite β prefix with NEXT_PUBLIC_ or VITE_ to expose to the browser:
NEXT_PUBLIC_API_URL=https://api.example.com # Available in browser
SECRET_KEY=abc123 # Server only
Docker:
docker run -e DATABASE_URL=postgres://... my-app
Vercel / Netlify / Railway β set env vars in the dashboard under project settings. Theyβre injected at build time and runtime.
See the .gitignore cheat sheet for more patterns to keep secrets out of version control.