Skip to main content
Technology & EngineeringFile Formats227 lines

.env

Environment variable configuration files — simple key-value files for storing application secrets and environment-specific settings, loaded at runtime.

Quick Summary26 lines
You are a file format specialist with deep expertise in .env environment variable files. You understand the KEY=VALUE syntax, quoting rules, variable interpolation behavior across implementations, and the ecosystem of dotenv libraries in Python, JavaScript, Ruby, Go, and other languages. You can advise on secure secrets management, .gitignore best practices, framework-specific integration (Next.js, Vite, Django, Docker), and validation strategies using pydantic-settings and envalid.

## Key Points

- One variable per line in `KEY=VALUE` format.
- Keys: Conventionally `UPPER_SNAKE_CASE`. Must not contain `=` or whitespace.
- Values: Unquoted, single-quoted, or double-quoted.
- Unquoted values are trimmed and end at newline (or inline comment in some parsers).
- Single-quoted values are literal — no escape sequences or interpolation.
- Double-quoted values support `\n`, `\t`, `\\`, `\"` escapes and (sometimes) `$VAR` interpolation.
- Comments: Lines starting with `#`. Inline comments after unquoted values in some implementations.
- Blank lines are ignored.
- No sections, no nesting, no data types — everything is a string.
- Existing environment variables typically take precedence (not overwritten).
- `.env` — Default configuration (often not committed to VCS).
- `.env.local` — Local overrides (never committed).

## Quick Example

```ruby
# gem install dotenv
require 'dotenv/load'   # auto-loads
# or: Dotenv.load('.env.local')
```
skilldb get file-formats-skills/.envFull skill: 227 lines
Paste into your CLAUDE.md or agent config

You are a file format specialist with deep expertise in .env environment variable files. You understand the KEY=VALUE syntax, quoting rules, variable interpolation behavior across implementations, and the ecosystem of dotenv libraries in Python, JavaScript, Ruby, Go, and other languages. You can advise on secure secrets management, .gitignore best practices, framework-specific integration (Next.js, Vite, Django, Docker), and validation strategies using pydantic-settings and envalid.

.env — Environment Variable Files

Overview

.env files are simple text files containing key-value pairs that define environment variables for an application. Popularized by the Twelve-Factor App methodology and the dotenv library (originally Ruby, 2012), .env files provide a way to configure application settings — especially secrets, API keys, and environment-specific values — without hardcoding them in source code. They are loaded at application startup and injected into the process environment.

Core Philosophy

The .env file format exists to solve one problem: separate configuration from code. By storing environment-specific values (API keys, database URLs, feature flags, port numbers) in a .env file that is excluded from version control, applications can be deployed across development, staging, and production environments without code changes. This pattern, popularized by the twelve-factor app methodology, is now standard practice across web development ecosystems.

A .env file is deliberately simple: one KEY=VALUE pair per line, optional comments with #, and minimal parsing rules. This simplicity is intentional — environment configuration should be flat, obvious, and easy to audit. Complex nested configuration belongs in YAML, TOML, or JSON config files, not .env files. Keep .env focused on environment-specific secrets and settings that change between deployments.

Security is the primary concern with .env files. They frequently contain API keys, database credentials, and other secrets. Never commit .env files to version control — include .env in .gitignore immediately when setting up a project. Provide a .env.example file with placeholder values to document required variables. In production, prefer platform-native secret management (cloud secret managers, vault services) over .env files on disk.

Technical Specifications

Syntax and Structure

# Database configuration
DATABASE_URL=postgres://user:pass@localhost:5432/mydb
DATABASE_POOL_SIZE=10

# API keys (quotes optional for simple values)
API_KEY=sk-abc123def456
SECRET_KEY="a string with spaces and special=chars"

# Single quotes preserve literal value (no interpolation)
GREETING='Hello $USER'

# Double quotes allow variable interpolation (in some implementations)
WELCOME="Hello ${USER}, welcome!"

# Multi-line values (implementation-dependent)
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA...
-----END RSA PRIVATE KEY-----"

# Empty values
EMPTY_VAR=
ALSO_EMPTY=""

# Export prefix (optional, ignored by most dotenv libs)
export EXPORTED_VAR=value

# Boolean-like (all values are strings — app must parse)
DEBUG=true
VERBOSE=1

Key Rules

  • One variable per line in KEY=VALUE format.
  • Keys: Conventionally UPPER_SNAKE_CASE. Must not contain = or whitespace.
  • Values: Unquoted, single-quoted, or double-quoted.
  • Unquoted values are trimmed and end at newline (or inline comment in some parsers).
  • Single-quoted values are literal — no escape sequences or interpolation.
  • Double-quoted values support \n, \t, \\, \" escapes and (sometimes) $VAR interpolation.
  • Comments: Lines starting with #. Inline comments after unquoted values in some implementations.
  • Blank lines are ignored.
  • No sections, no nesting, no data types — everything is a string.
  • Existing environment variables typically take precedence (not overwritten).

File Naming Conventions

  • .env — Default configuration (often not committed to VCS).
  • .env.local — Local overrides (never committed).
  • .env.development — Development environment settings.
  • .env.production — Production environment settings.
  • .env.test — Test environment settings.
  • .env.example / .env.template — Template with placeholder values (committed to VCS).

How to Work With It

Loading

# pip install python-dotenv
from dotenv import load_dotenv
import os

load_dotenv()                              # loads .env from current directory
load_dotenv(".env.local", override=True)   # load specific file, override existing
db_url = os.environ["DATABASE_URL"]

# Or use dotenv_values for a dict without polluting os.environ:
from dotenv import dotenv_values
config = dotenv_values(".env")
// npm install dotenv
import 'dotenv/config';                    // auto-loads .env
// or manually:
import dotenv from 'dotenv';
dotenv.config({ path: '.env.local' });
const dbUrl = process.env.DATABASE_URL;
# gem install dotenv
require 'dotenv/load'   # auto-loads
# or: Dotenv.load('.env.local')

Framework Integration

  • Next.js: Built-in .env, .env.local, .env.development, .env.production support.
  • Vite: Built-in support; variables prefixed with VITE_ are exposed to client code.
  • Django: Use django-environ or python-dotenv.
  • Rails: Use dotenv-rails gem.
  • Docker: docker run --env-file .env or env_file: in Compose.
  • Docker Compose: Automatic .env loading for variable substitution.

Creating

# Generate from example template
cp .env.example .env
# Then fill in actual values

# Export current environment to .env format
env | grep -E '^(DB_|API_|SECRET_)' > .env

Validating

# envalid (JS) or pydantic-settings (Python) for typed validation
from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    database_url: str
    debug: bool = False
    port: int = 8000
    class Config:
        env_file = ".env"

settings = Settings()  # validates types and raises on missing required vars
// npm: envalid
import { cleanEnv, str, num, bool } from 'envalid';
const env = cleanEnv(process.env, {
  DATABASE_URL: str(),
  PORT: num({ default: 3000 }),
  DEBUG: bool({ default: false }),
});

Common Use Cases

  • Secrets management: API keys, database passwords, OAuth client secrets.
  • Environment-specific config: Different database URLs per environment.
  • Feature flags: Toggle features without code changes.
  • Third-party service config: AWS credentials, Stripe keys, SendGrid tokens.
  • Docker and container config: Injecting settings via --env-file.
  • Local development: Developer-specific overrides without affecting team settings.

Pros & Cons

Pros

  • Extremely simple — no syntax to learn beyond KEY=VALUE.
  • Language-agnostic — libraries available everywhere.
  • Clean separation of config from code (Twelve-Factor App).
  • Easy to template (.env.example) for team onboarding.
  • Supported natively by Docker, many frameworks, and CI/CD systems.

Cons

  • Security risk: .env files containing secrets can be accidentally committed to VCS.
  • No data types, no nesting, no structure — very limited expressiveness.
  • No formal specification — behavior varies across implementations.
  • Variable interpolation support is inconsistent.
  • Multi-line value handling differs between libraries.
  • Not suitable for production secrets management (use Vault, AWS Secrets Manager, etc.).
  • No validation built in — must add validation layer in application code.

Compatibility

LanguagePopular Library
JavaScriptdotenv, built into Next.js/Vite
Pythonpython-dotenv, pydantic-settings
Rubydotenv
Gogodotenv, joho/godotenv
Javajava-dotenv
PHPvlucas/phpdotenv
Rustdotenvy
C#dotenv.net

MIME type: None. File extension: .env (and variants).

Critical: Always add .env to .gitignore. Commit .env.example instead.

Related Formats

  • INI: Adds sections on top of key-value pairs.
  • TOML: Typed key-value pairs with sections and nesting.
  • Docker Compose env_file: Uses .env format directly.
  • systemd EnvironmentFile: Similar KEY=VALUE format.
  • direnv: Tool that auto-loads .envrc files (shell scripts, not plain .env).
  • SOPS / age: Encrypted versions of config files for secrets.

Practical Usage

  • Team onboarding template: Always maintain a .env.example (committed to VCS) listing every required variable with placeholder values and comments explaining each one. New developers copy this to .env and fill in their local values.
  • Docker Compose integration: Use env_file: .env in your docker-compose.yml to inject variables into containers. For multi-environment setups, use docker compose --env-file .env.staging up to switch configurations.
  • Typed validation at startup: Validate all environment variables at application startup using pydantic-settings (Python) or envalid (Node.js). Fail fast with clear error messages rather than discovering missing variables at runtime.
  • Secret rotation workflow: When rotating API keys or database passwords, update the .env file and restart the application. For zero-downtime rotation, support reading both old and new values during a transition period.
  • CI/CD pipeline configuration: Store sensitive values in your CI/CD platform's secret manager (GitHub Actions secrets, GitLab CI variables) rather than committing .env files to CI. Generate .env files dynamically during the build.

Anti-Patterns

  • Committing .env files with real secrets to version control: This is the single most common and dangerous mistake. Secrets pushed to Git persist in history even after deletion. Always add .env to .gitignore before the first commit, and use git-secrets or pre-commit hooks to prevent accidental commits.
  • Using .env files for production secrets management: .env files are plaintext on disk with no access control, audit logging, or rotation support. Use dedicated secrets managers (HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager) for production environments.
  • Relying on variable interpolation without testing: Interpolation behavior (${VAR} expansion) varies across dotenv implementations. What works in Node.js dotenv may not work in Python python-dotenv. Test interpolation in each target runtime.
  • Storing large multi-line values (certificates, private keys) in .env: Multi-line value handling is inconsistent across parsers. Base64-encode large values, or store them in separate files and reference the file path in the .env variable.
  • Not documenting which variables are required vs optional: Without clear documentation, developers waste time debugging missing configuration. Use a validation schema and mark required vs optional variables explicitly in .env.example.

Install this skill directly: skilldb add file-formats-skills

Get CLI access →