Keep .env.example in sync with .env and validate environment variables before boot. Detect missing, extra and empty env vars in CI. A zero-dependency dotenv-linter alternative that stops apps crashing on a missing variable.
Project description
🔐 envsync
Keep .env.example in sync with .env — and never boot with a missing variable again.
The number one "works on my machine" bug: someone adds STRIPE_KEY to their .env,
forgets to update .env.example, and a teammate's app crashes with a cryptic error.
envsync fixes both sides of that — automatically, with zero dependencies.
Installed from PyPI as
dotenv-sync; it gives you theenvsynccommand (and adotenv-syncalias).
The problem
.env.example is the contract every teammate copies. But it drifts the instant someone
adds a variable to their local .env and forgets the template:
- New devs
cp .env.example .env, run the app → crash, because a required key was never in the template. - Code reviews can't see that a PR introduced a new required env var.
- Secrets occasionally get pasted into
.env.exampleby hand and leak into git.
The fix
uvx dotenv-sync # add new keys from .env to .env.example (values stripped)
uvx dotenv-sync check # CI: fail if the template drifted
uvx dotenv-sync validate # preflight: fail if your local .env is missing required keys
$ uvx dotenv-sync
✔ added 2 key(s) to .env.example:
+ STRIPE_KEY
+ REDIS_URL
$ uvx dotenv-sync validate
✖ missing 1 required key(s) (declared in .env.example):
✖ DATABASE_URL
Your app may crash on boot. Fill these in .env (copy from .env.example).
- ✅ Never leaks values. Only keys are written to
.env.example(KEY=), and your curated placeholders/comments are preserved. - ✅ Non-destructive & idempotent. Existing lines are never touched; running twice is a no-op.
- ✅ Zero dependencies. Installs instantly, nothing to audit.
Install
uvx dotenv-sync # run without installing (uv)
pipx install dotenv-sync # or install the CLI globally
pip install dotenv-sync # or into your project
Commands
| Command | What it does |
|---|---|
envsync / envsync sync |
Append keys in .env missing from .env.example (values stripped) |
envsync check |
CI mode — exit 1 if .env.example is missing keys from .env |
envsync validate |
Preflight — exit 1 if your .env is missing keys the template requires |
envsync init |
Create .env.example from .env (values stripped) or a starter |
Options: --env <file>, --example <file>, --cwd <dir>, --allow-empty, --process
(also accept keys from the process environment during validate), --quiet.
Use it in CI
# .github/workflows/env.yml
name: env
on: [push, pull_request]
jobs:
envsync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- run: uvx dotenv-sync check # fail if someone forgot to update .env.example
Pre-commit hook:
# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: envsync
name: envsync check
entry: envsync check
language: system
pass_filenames: false
Preflight before your app starts
Catch missing config before a confusing runtime crash — e.g. in a container entrypoint:
envsync validate && python -m myapp
Python API
from envsync import validate
result = validate()
if not result.ok:
raise SystemExit(f"Missing env: {result.missing}")
How keys are parsed
KEY=VALUE, export KEY=VALUE, single/double-quoted values, # comments, and blank
lines. Unrecognized lines are preserved verbatim, so envsync never mangles your files.
FAQ
How do I keep .env.example in sync with .env in Python?
Run uvx dotenv-sync (or pip install dotenv-sync then envsync). It adds any key in
.env that's missing from .env.example, values stripped, preserving your comments. Add
uvx dotenv-sync check to CI so the build fails when the template drifts.
How do I validate required environment variables before the app starts?
uvx dotenv-sync validate reads the keys declared in .env.example and exits 1 listing
any missing or empty ones in your .env or process environment — a preflight that catches
misconfiguration before a confusing runtime crash.
Does it leak secrets into .env.example?
Never. Only keys are written (KEY=); values are always stripped and existing lines are
preserved.
dotenv-sync vs dotenv-linter / python-dotenv / pydantic-settings?
python-dotenv/pydantic-settings load env vars; dotenv-sync keeps the template
honest and validates it — sync, CI drift-check, and preflight validation, with zero
dependencies. It's the Python twin of the npm envsync package (byte-identical output).
Also on npm
JavaScript project? The same tool ships on npm — npx envsync.
License
MIT — free for personal and commercial use.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dotenv_sync-1.0.0.tar.gz.
File metadata
- Download URL: dotenv_sync-1.0.0.tar.gz
- Upload date:
- Size: 5.6 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7b8ad4c0591c3550d80cd44443fe706aaa239e7d9aa625a49f7a4b5bfeac575e
|
|
| MD5 |
ff0d5ac3a82d270a5f9feaba57f9c566
|
|
| BLAKE2b-256 |
f8df19bbdb7a8c3058ed1893c1f8f04a523c53f00dee2f8bfaff7d4e80829cef
|
File details
Details for the file dotenv_sync-1.0.0-py3-none-any.whl.
File metadata
- Download URL: dotenv_sync-1.0.0-py3-none-any.whl
- Upload date:
- Size: 5.6 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aa82de8f8f4cc3a926eba2f7f18bec85583712d9b3d46f2c0b6e858d85fb4b89
|
|
| MD5 |
bd148b604fef8da959bbd8886c7fa3eb
|
|
| BLAKE2b-256 |
859aa21e61e385545b4a6191b31bddc4622d130fb2b7ecb5fd7d07d944cb3b0c
|