Sync and Secure Environment Variables Across Environments
Environment variable management is one of those problems every team has and nobody solves properly. Keys pile up across .env files. Secrets get rotated in prod but not staging. Someone pushes a .env.prod to GitHub by accident. A deployment breaks because STAGING_DB_URL was still pointing to a read replica that was decommissioned last month.
Envault is a CLI tool that brings order to your environment variables. It diffs environments, syncs with conflict resolution, rotates secrets with smart type inference, and integrates with AWS SSM, HashiCorp Vault, Doppler, and 1Password.
1. Install Envault
Or from GitHub:
Verify:
2. Initialize a Project
Create a configuration file that defines your environments:
This generates a .envault.yml file in the current directory:
project: my-project
environments:
dev: .env.dev
staging: .env.staging
prod: .env.prod
conflict_strategy: source_wins
skip_keys:
- COMMIT_SHA
- BUILD_NUMBER
Now create some sample .env files to simulate real environments. Save as .env.dev:
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp_dev
DB_PASSWORD=dev_password_123
API_KEY=dev_key_abc123
LOG_LEVEL=debug
FEATURE_NEW_CHECKOUT=true
FEATURE_DARK_MODE=false
CACHE_TTL=300
.env.staging:
DB_HOST=staging-db.internal
DB_PORT=5432
DB_NAME=myapp_staging
DB_PASSWORD=staging_password_456
API_KEY=staging_key_def456
LOG_LEVEL=info
CACHE_TTL=600
.env.prod with some drift and a missing key:
DB_HOST=prod-db.internal
DB_PORT=5432
DB_NAME=myapp_prod
DB_PASSWORD=prod_password_789
LOG_LEVEL=warn
CACHE_TTL=1200
API_KEY is missing from prod entirely — that endpoint will 401. FEATURE_NEW_CHECKOUT and FEATURE_DARK_MODE were added to dev but never promoted. CACHE_TTL differs across all three. These are the kinds of silences that cause "works on my machine" bugs in production.
3. Diff Environments
See exactly what's different between any two environments:
Envault produces a formatted diff showing keys that are:
- Only in source — present in dev, missing in prod
- Only in target — present in prod, missing in dev
- Different values — same key, different values
═══ Diff: dev → prod ═══
Only in dev:
FEATURE_NEW_CHECKOUT=true
FEATURE_DARK_MODE=false
API_KEY=dev_key_abc123
Only in prod:
(none)
Different values:
DB_HOST: localhost → prod-db.internal
DB_NAME: myapp_dev → myapp_prod
DB_PASSWORD: dev_password_123 → prod_password_789
LOG_LEVEL: debug → warn
CACHE_TTL: 300 → 1200
You can also diff two .env files directly without a project config:
4. Sync Environments
Promote changes from one environment to another with controlled conflict resolution:
Always dry-run first. The output shows exactly what would change:
═══ Dry Run: sync dev → prod ═══
Would add:
FEATURE_NEW_CHECKOUT=true
FEATURE_DARK_MODE=false
API_KEY=dev_key_abc123
Would update:
LOG_LEVEL: warn → debug
CACHE_TTL: 1200 → 300
When you're ready:
Envault also supports different conflict resolution strategies:
--strategy target_wins when syncing to production — you don't want to accidentally overwrite prod credentials with dev values. Use the default source_wins when promoting staging → prod after QA sign-off.
5. Rotate Secrets with Smart Type Inference
Rotating secrets manually is error-prone — someone has to generate a value, update every .env file, and make sure the format is correct. Envault's rotate command handles this automatically:
Envault inspects the key name and generates a cryptographically secure value with the correct format:
Key: DB_PASSWORD
Type: database password (inferred from key name)
Generated: L8#mKx2$rT9!qW5@nB4&
Updated in: .env.dev, .env.staging, .env.prod
Smart rotation infers the correct format from the variable name:
| Key Pattern | Generated Format | Example |
|---|---|---|
DB_PASSWORD, DATABASE_URL | No ambiguous chars, mixed case + symbols | L8#mKx2$rT9!qW5@nB4& |
API_KEY, STRIPE_SECRET | Prefixed API key with version | sk_live_v2_8aF3...eR7k |
JWT_SECRET | 256-bit base64 | dGhpcyBpcyBhIDI1Ni1iaXQgc2VjcmV0 |
WEBHOOK_SECRET | Long hex key | a1b2c3d4e5f6...7890 |
| Everything else | 32-char random string | Zx9pQ2mN5kL7wR4t... |
Additional rotation options:
--dry-run --show lets you preview the new value before it's applied. Always rotate secrets during maintenance windows and verify downstream services after.
6. Secret Store Integrations
Envault can sync with external secret stores — useful when your team uses a centralized vault:
Supported stores: AWS SSM Parameter Store, HashiCorp Vault, Doppler, and 1Password. Configure store connections in your .envault.yml file.
7. CI/CD Integration
Use Envault to verify environment consistency in CI:
# .github/workflows/env-check.yml
name: Environment Variable Check
on:
pull_request:
paths:
- '.env*'
- '.envault.yml'
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Envault
run: pip install envault
- name: Check for drift
run: |
echo "=== Comparing staging vs prod ==="
envault diff staging prod || echo "⚠ Drift detected!"
- name: Verify no secrets leaked
run: |
if grep -r 'password\|secret\|key' .env.* --include='.env*' 2>/dev/null; then
echo "::warning::Potential secret in .env file"
fi
For deployments, rotate secrets before release:
# Rotate critical secrets before a production deploy
envault rotate DB_PASSWORD --env prod
envault rotate API_KEY --env prod --dry-run
Command Reference
| Command | Description |
|---|---|
envault init <project> | Create .envault.yml config file |
envault diff <src> <tgt> | Show key-level differences between environments |
envault diff-files <f1> <f2> | Diff two .env files directly |
envault sync <src> <tgt> | Sync variables with conflict resolution |
envault sync --dry-run | Preview changes before applying |
envault sync --strategy target_wins | Keep target values on conflict |
envault rotate <key> | Rotate a secret with smart type inference |
envault rotate --dry-run --show | Preview rotated value without applying |
envault rotate-all --env prod | Rotate all secrets in an environment |
envault store list/get/set | Interact with external secret stores |
Real-World Scenario: The Stale Env Rotation
A SaaS team rotated their production database password after a security audit. They updated .env.prod, deployed, and everything worked. Three weeks later, a developer ran the staging environment — and it connected to production. The staging .env file still had the old, un-rotated password that happened to match a legacy read replica's credentials. The incident took two hours to debug.
With Envault, they would have run envault rotate DB_PASSWORD which updates all environments simultaneously. The diff command would have caught the staging-prod mismatch before it caused an incident.
Next Steps
Envault is one of 10 tools in the Revenue Holdings suite. Pair it with other tools that protect your deployments:
- ConfigDrift — Detect config drift across environments (tutorial)
- API Contract Guardian — Catch breaking API schema changes in PRs
- DeployDiff — Preview infra costs and blast radius (tutorial)
- APIGhost — Mock APIs from OpenAPI specs (tutorial)
Get Notified About New Tutorials
DevOps guides, env management tips, and product updates.