Scan .env files against .env.example — detect missing variables, extras, and leaked secrets.
Every project uses .env files. But .env drifts from .env.example, secrets leak into version control, and new team members waste hours figuring out which variables they need. envsurf catches all of that in one pass.
pip install envsurf# From your project root — finds all .env/.env.example pairs recursively
envsurf .
# Explicit paths
envsurf --env .env --example .env.example
# CI mode — exit code 1 if any issues found
envsurf --strict .
# Skip secret scanning
envsurf --no-secrets .
# JSON output for tooling integration
envsurf --json .
# Ignore specific keys
envsurf --ignore DEBUG_MODE,LOCAL_TEST .# Auto-generate .env from .env.example, adding missing keys with TODO comments
envsurf fix
# Dry run — show what would change without writing
envsurf fix --dry-run
# Custom paths
envsurf fix --env .env.local --example .env.example# Scan your source code for env var references and generate .env.example
envsurf init
# Custom output path
envsurf init -o .env.template
# Force overwrite existing file
envsurf init --force
# Dry run
envsurf init --dry-run| Check | Description |
|---|---|
| Missing vars | Keys in .env.example but not in .env — you forgot to set them |
| Extra vars | Keys in .env but not in .env.example — undocumented config |
| Leaked secrets | Values that look like real API keys, AWS keys, GitHub tokens, JWTs, URLs with embedded credentials |
| Parse errors | Malformed lines that can't be parsed |
envsurf uses heuristics to detect real secrets while ignoring placeholders:
- ✅ Catches: AWS keys (
AKIA...), GitHub tokens (ghp_...), JWTs, URLs with embedded credentials, high-entropy strings - ❌ Skips:
changeme,<your-key>,[insert-here],${VAR},$VAR, empty values, booleans
envsurf init scans your codebase for environment variable references across 11 languages:
Python · JavaScript/TypeScript · Ruby · Go · Rust · Shell · Java · PHP · C# · Docker Compose · YAML
envsurf — .env file surface scanner
.env vs .env.example
Missing in .env:
• REDIS_URL (example: 'redis://localhost:6379')
Extra in .env (not in example):
• DEBUG_MODE
Potential secrets:
🔴 L4: AWS_SECRET_ACCESS_KEY (aws_key)
🟡 L7: API_TOKEN (secret_key_name)
Found 3 issue(s) across 1 pair(s)
envsurf --json .Returns structured JSON with all findings — perfect for CI pipelines, editors, and tooling integration.
# GitHub Actions
- name: Check .env drift and secrets
run: |
pip install envsurf
envsurf --strict .| Command | Description |
|---|---|
envsurf (or envsurf scan) |
Scan .env files for drift, extras, and secrets |
envsurf fix |
Generate/update .env from .env.example |
envsurf diff |
Compare two .env files (staging vs production) |
envsurf init |
Generate .env.example from source code |
# Compare staging vs production .env files
envsurf diff .env.staging .env.production
# JSON output
envsurf diff .env.staging .env.production --jsonShows variables that are:
- Only in A — defined in first file but not second
- Only in B — defined in second file but not first
- Value differences — same key, different values
envsurf works with zero configuration. Just:
- Keep
.env.examplealongside your.envfiles - Run
envsurf .
git clone https://github.com/jlaportebot/envsurf.git
cd envsurf
pip install -e ".[dev]"
pytestenvsurf diff— compare two arbitrary .env files (staging vs production, etc.)- Dockerfile ENV scanning — detects
ENV KEY=valuein Dockerfiles - Bug fix: removed duplicate Go
os.Getenvregex with wrong closing bracket - 62 tests passing
envsurf fix— auto-generate .env from .env.example with TODO placeholdersenvsurf init— scan source code to generate .env.example (11 languages)- JSON output —
--jsonflag for CI/tooling integration --ignoreflag — suppress known extras/secrets by key name- Backward-compatible:
envsurf .still works as before
- Initial release: scan, secret detection, strict mode