Skip to content

envdrift diff

Compare two .env files and show differences.

Synopsis

envdrift diff ENV1 ENV2 [OPTIONS]

Description

The diff command compares two .env files and shows:

  • Added variables - Present in ENV2 but not ENV1
  • Removed variables - Present in ENV1 but not ENV2
  • Changed variables - Different values between files
  • Unchanged variables - Same in both (with --include-unchanged)

This is useful for:

  • Reviewing differences between development and production
  • Auditing environment changes before deployment
  • Detecting drift between team members' environments

Arguments

Argument Description
ENV1 Path to first .env file (baseline)
ENV2 Path to second .env file (comparison)

Options

--schema, -s

Schema for sensitive field detection. When provided, sensitive fields are masked in output.

envdrift diff .env.dev .env.prod --schema config.settings:Settings

--service-dir, -d

Directory to add to Python's sys.path for schema imports.

envdrift diff .env.dev .env.prod -s config.settings:Settings -d /app/backend

--format, -f

Output format: table (default) or json.

# Human-readable table (default)
envdrift diff .env.dev .env.prod --format table

# Machine-readable JSON
envdrift diff .env.dev .env.prod --format json

--show-values

Show actual values instead of masking them. Use with caution - this may expose secrets!

envdrift diff .env.dev .env.prod --show-values

By default, values are shown but sensitive fields (when schema is provided) are masked.

--include-unchanged

Include variables that are identical in both files.

envdrift diff .env.dev .env.prod --include-unchanged

--normalize, --strict

Normalize values before comparing so trivially-equivalent strings don't show up as drift. Enabled by default; pass --strict to fall back to raw string compare.

When --normalize is in effect, two values are considered equal if:

  • they match after stripping leading/trailing whitespace (DATABASE_URL="foo " vs DATABASE_URL=foo),
  • both look like booleans (true|false|yes|no|on|off|1|0, any case) and share the same truthiness (DEBUG=true vs DEBUG=True), or
  • both look like JSON lists/objects and parse to the same structure, regardless of single- vs double-quote style (CORS_ORIGINS=["http://x"] vs CORS_ORIGINS=['http://x']).

When --schema is also passed, each value is additionally coerced through the matching Pydantic field type (bool, int, list[str], Literal[...], etc.) before comparison. Variables not in the schema, or values that fail Pydantic validation, fall back to the universal rules above.

# Default — normalization on
envdrift diff .env.dev .env.prod

# Disable normalization for strict raw-string compare
envdrift diff .env.dev .env.prod --strict

# Schema-aware coercion plus normalization
envdrift diff .env.dev .env.prod --schema config.settings:Settings

Displayed values are always the original ones from the file — normalization only affects the changed / unchanged classification, never what you see in the table or JSON output.

Examples

Basic Comparison

envdrift diff .env.development .env.production

Output:

╭────────────────────── envdrift diff ──────────────────────╮
│ Comparing: .env.development vs .env.production            │
╰───────────────────────────────────────────────────────────╯

┏━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓
┃ Variable        ┃ .env.development┃ .env.production ┃ Status   ┃
┡━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━┩
│ DEBUG           │ true            │ false           │ changed  │
│ LOG_LEVEL       │ DEBUG           │ WARNING         │ changed  │
│ SENTRY_DSN      │ (missing)       │ https://...     │ added    │
│ DEV_ONLY_VAR    │ testing         │ (missing)       │ removed  │
└─────────────────┴─────────────────┴─────────────────┴──────────┘

Summary: 2 changed, 1 added, 1 removed

Drift detected between environments

JSON Output for CI/CD

envdrift diff .env.dev .env.prod --format json

Output:

{
  "env1": ".env.development",
  "env2": ".env.production",
  "summary": {
    "added": 1,
    "removed": 1,
    "changed": 2,
    "has_drift": true
  },
  "differences": [
    {
      "name": "DEBUG",
      "type": "changed",
      "value_env1": "true",
      "value_env2": "false",
      "sensitive": false
    },
    {
      "name": "SENTRY_DSN",
      "type": "added",
      "value_env1": null,
      "value_env2": "https://...",
      "sensitive": true
    }
  ]
}

With Schema for Sensitive Detection

envdrift diff .env.dev .env.prod --schema config.settings:Settings

Sensitive fields (marked with json_schema_extra={"sensitive": True}) are labeled in output.

Show All Variables

envdrift diff .env.dev .env.prod --include-unchanged

Expose Values (Use with Caution)

envdrift diff .env.dev .env.prod --show-values

CI/CD Drift Detection

# GitHub Actions - Comment on PR with drift report
- name: Check drift
  id: drift
  run: |
    envdrift diff .env.development .env.production --format json > drift.json

- name: Comment on PR
  uses: actions/github-script@v7
  with:
    script: |
      const fs = require('fs');
      const drift = JSON.parse(fs.readFileSync('drift.json', 'utf8'));

      if (drift.has_drift) {
        github.rest.issues.createComment({
          issue_number: context.issue.number,
          owner: context.repo.owner,
          repo: context.repo.repo,
          body: `## Environment Drift Detected\n\n` +
                `- Added: ${drift.summary.added}\n` +
                `- Removed: ${drift.summary.removed}\n` +
                `- Changed: ${drift.summary.changed}`
        });
      }

Diff Types

Type Description
added Variable exists in ENV2 but not ENV1
removed Variable exists in ENV1 but not ENV2
changed Variable exists in both but with different values
unchanged Variable is identical in both files

Use Cases

Pre-Deployment Review

Before deploying, compare staging and production:

envdrift diff .env.staging .env.production

Onboarding New Team Members

Compare your .env with the template:

envdrift diff .env.example .env

Detecting Configuration Drift

Monitor differences across environments:

for env in staging production; do
  echo "=== Development vs $env ==="
  envdrift diff .env.development .env.$env
done

See Also