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=3000
Quoted 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=true
Language 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 on process.env.dev: