Diff, scan, check and validate
.envfiles — CLI + Node.js library.
Every Node.js project suffers from the same .env problems:
- App crashes in production because someone forgot to add a new variable
.env.exampleis months out of date- CI passes but deployment fails with
Cannot read property of undefined - No one knows which variables are required vs optional
env-surgeon solves all of this with a handful of commands.
npx env-surgeon <command> # run without installing
npm install -D env-surgeon # dev dependency (recommended)
npm install -g env-surgeon # globalenv-surgeon diff .env .env.productionenv-surgeon scan ./src --output .env.exampleFinds process.env.X, import.meta.env.X, Deno.env.get('X'), Bun.env.X,
and destructured const { FOO } = process.env.
env-surgeon check
env-surgeon check --env .env --template .env.example --strictenv-surgeon validate --env .env --schema .env.schema.jsonSupports types: string, number, boolean, url, email, json, port, duration, secret, array.
Zod is supported out of the box — point --schema at a .js/.mjs file whose default export is a Zod object schema:
// env.schema.js
import { z } from 'zod'
export default z.object({
PORT: z.coerce.number().min(1024),
DATABASE_URL: z.string().url(),
LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).optional(),
})env-surgeon validate --env .env --schema env.schema.jszod is an optional peer dependency — install it only if you use this path.
env-surgeon print --env .env.local --env .env --expand
env-surgeon print --env .env --reveal # actual values to stdoutenv-surgeon unused ./src --env .envenv-surgeon init --env .env --output .env.schema.jsonInfers types (number, boolean, URL, email, JSON, port, duration) from values.
| Option | Description |
|---|---|
--json |
Output as JSON |
--reporter <fmt> |
Output format: text, json, junit |
--silent |
Exit code only, no output |
--expand |
Expand ${VAR} references (dotenv-expand semantics) |
--watch |
Re-run on file changes (check, validate) |
--strict |
Fail on extra keys not in template/schema |
--auto-env |
Auto-select .env files by NODE_ENV |
import { diff, scan, check, validate } from 'env-surgeon'
const result = await diff('.env', '.env.production')
const found = await scan('./src')
const status = await check({ template: '.env.example', env: '.env' })
const errors = await validate({
env: '.env',
schema: { PORT: { type: 'number', required: true } },
})import {
parseEnvFile, parseEnvString, readEnvCascade,
defaultEnvCascade, UserError,
} from 'env-surgeon'your-project/
├── .env ← local, git-ignored
├── .env.example ← committed, generated by `scan`
└── .env.schema.json ← committed, generated by `init`
{
"scripts": {
"env:sync": "env-surgeon scan ./src --output .env.example",
"env:check": "env-surgeon check",
"prestart": "env-surgeon check",
"prebuild": "env-surgeon validate"
}
}- uses: env-surgeon/env-surgeon@v1
with:
command: check
env: .env.ci
template: .env.example
strict: trueSee docs/guide.md for:
- Environment adaptation (stdin, cascades, Windows, Bun/Deno)
- Config file reference
- NestJS / dotenv-expand compatibility
.env.vaultencrypted file support- Watch mode
- CI reporters (JUnit XML)
- Execution model and pitfalls
- Node.js >= 20
MIT