A .env file is a simple text file that stores environment variables as key-value pairs. It has become the de facto standard for local development configuration across every major framework — from Node.js and Python to Go, Ruby, and Docker. Instead of hardcoding secrets and config into your source code, you place them in a .env file that is loaded at startup and kept out of version control.
What is a .env file?
The concept was popularized by the dotenv library for Node.js, though implementations now exist for virtually every language. The idea is rooted in the 12-Factor App methodology, which states that configuration should be strictly separated from code and stored in the environment.
When your application starts, a dotenv parser reads the .env file and injects its key-value pairs into the process environment. In Node.js they become available via process.env, in Python via os.environ, and so on. The file itself is never deployed — it exists only on the developer's machine or in a CI pipeline.
Syntax Reference
The .env format is intentionally simple, but there are a few rules worth knowing:
Basic assignment
Keys are uppercase by convention. There must be no spaces around the = sign.
DATABASE_HOST=localhost
PORT=3000Quoted values
Double quotes allow spaces and special characters. Single quotes treat the value as a literal string with no interpolation.
GREETING="Hello, World!"
REGEX='\d+\.\d+'Comments and empty values
Lines starting with # are comments. KEY= sets an empty string, as does KEY="".
# Database config
DB_PASSWORD=
DB_NAME=""Multiline and variable expansion
Some parsers support multiline values in double quotes and variable expansion with ${} syntax.
PRIVATE_KEY="-----BEGIN RSA KEY-----
MIIBogIBAAJBALRi...
-----END RSA KEY-----"
BASE_URL=https://api.example.com
FULL_URL=${BASE_URL}/v1/usersHere is a complete example:
# App
NODE_ENV=development
PORT=3000
# Database
DATABASE_URL="postgres://user:pass@localhost:5432/mydb"
DATABASE_POOL_SIZE=10
# External APIs
STRIPE_SECRET_KEY=sk_test_abc123
SENDGRID_API_KEY="SG.xxxx"
# Feature flags
ENABLE_CACHE=trueLanguage Examples
Every major language has a dotenv library. Here is how to get started in the most popular ones:
| Language | Setup | Access |
|---|---|---|
| Node.js | import 'dotenv/config' | process.env.KEY |
| Python | from dotenv import load_dotenv | os.getenv('KEY') |
| Go | godotenv.Load() | os.Getenv("KEY") |
| Docker | env_file: .env in compose | Injected into container environment |
Best Practices
- Never commit .env to git. Add it to your
.gitignoreimmediately. Leaked secrets are the number one cause of security incidents. - Provide a
.env.example. Commit a template file with dummy values so new team members know which variables are required. - Validate at startup. Fail fast if a required variable is missing or malformed. Libraries like
envalid(Node.js) orpydantic-settings(Python) make this easy. - Use typed configuration objects. Instead of sprinkling
process.env.Xthroughout your codebase, parse variables into a typed config object at the entry point. - Separate secrets from config. Non-sensitive values like
PORTorLOG_LEVELcan live in.env, but production secrets belong in a secrets manager (Vault, AWS SSM, etc.). - Use per-environment files. Many frameworks support
.env.development,.env.production, and.env.testto keep environments cleanly separated.
Common Pitfalls
- Whitespace around = —
KEY = valuewill break most parsers. Always useKEY=valuewith no spaces. - Unquoted values with # —
URL=https://x.com#anchorwill be truncated at the#because it is treated as an inline comment. Wrap the value in quotes instead. - .env is NOT for production secrets — In production, use your platform's native secrets management (e.g. Kubernetes Secrets, AWS Parameter Store, Cloudflare Worker secrets). A
.envfile on a server is a plain-text liability. - Parser inconsistencies — The dotenv format has no formal spec. Behavior differs between
dotenv,dotenvx, Docker, and shell sourcing. Always test with your specific parser.
Deep Dive
Ready to go further? Explore these resources: