Organize Your Configuration¶
Stable conventions for projects that use dotenv-fusion.
For executable integrations, use Workflows. For complete configuration examples, use Examples.
Project structure¶
project/
├── .env-fuse # source of truth
├── .env-fuse.local # local overrides, ignored by Git
├── .env # generated only when needed, ignored by Git
└── configs/
├── base.env
├── database-${ENV}.env
└── secrets-${ENV}.env
Source and generated files¶
- Treat
.env-fuseas the source of truth. - Generate
.envonly for tools that require plain dotenv. - Do not edit generated
.envby hand. - Keep generated
.envfiles ignored unless the project deliberately commits them.
Defaults and overrides¶
Put defaults in .env-fuse so tools can run without extra flags:
Override from the process environment when needed:
Prefer process environment overrides for direct shell and CI workflows. Use --var KEY=VALUE when the command itself must carry an explicit value, for example a one-off artifact generation detached from the current process:
Local overrides¶
Keep developer-specific values outside the shared source:
Team onboarding¶
Commit a template when every developer needs the same list of variables, then let each person create their ignored local override file:
Secrets¶
- Never commit secret values.
- Import secret files from ignored paths.
- Mark secret definitions explicitly when they exist in the schema.
Validation and documentation¶
Define important variables close to their source:
#@def ENV required=true validate=^(dev|staging|production)$
#@def PORT type=int default=3000 doc="HTTP listening port"
Use dotenv-fusion check for validation-only gates and dotenv-fusion docs to generate human-readable configuration documentation.
Imports and namespaces¶
Use prefixes when importing independent service configuration:
Keep import trees readable. A few clear files are easier to audit than many tiny fragments.
Trust boundary¶
Treat .env and .env-fuse files as trusted configuration. They can expand values from the process environment and, by default, import any file readable by the current process. Do not process an unreviewed configuration in an environment that exposes sensitive variables.
For defense in depth, --restrict-imports confines every resolved import to the parent directory of the main source file:
dotenv-fusion check -f .env-fuse --restrict-imports
dotenv-fusion fuse -f .env-fuse --restrict-imports
The same root remains active for nested imports, and symlinks cannot escape it. External imports remain allowed when the option is omitted for backward compatibility. This restriction does not prevent the source file from expanding process environment variables, so it is not a sandbox for untrusted files.
Adopt dotenv-fusion in an existing project¶
From a standard dotenv file, make .env-fuse the editable source:
If a tool still needs a plain .env file, generate it from .env-fuse instead of maintaining both files by hand:
From python-dotenv, keep the call site explicit and point it to .env-fuse:
Performance¶
- Keep import trees readable; avoid many tiny files for values that always change together.
- Use
dotenv-fusion loaddirectly when the process can receive exported variables. - Generate
.envonly for tools that need a file, and cache or reuse it inside the same job when multiple commands need the same resolved environment.