Skip to content

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
.gitignore
.env
.env.local
.env-fuse.local

Source and generated files

  • Treat .env-fuse as the source of truth.
  • Generate .env only for tools that require plain dotenv.
  • Do not edit generated .env by hand.
  • Keep generated .env files ignored unless the project deliberately commits them.

Defaults and overrides

Put defaults in .env-fuse so tools can run without extra flags:

.env-fuse
ENV=${ENV:-dev}
#@import configs/database-${ENV}.env

Override from the process environment when needed:

ENV=prod dotenv-fusion fuse -f .env-fuse -o .env

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:

dotenv-fusion fuse -f .env-fuse -o .env.production --var ENV=production

Local overrides

Keep developer-specific values outside the shared source:

.env-fuse
#@import configs/base.env
#@import .env-fuse.local mode=ifexist

Team onboarding

Commit a template when every developer needs the same list of variables, then let each person create their ignored local override file:

dotenv-fusion template -f .env-fuse > .env-fuse.example
cp .env-fuse.example .env-fuse.local

Secrets

  • Never commit secret values.
  • Import secret files from ignored paths.
  • Mark secret definitions explicitly when they exist in the schema.
.env-fuse
#@import secrets/${ENV}/api-keys.env
#@def API_KEY secret=true required=true

Validation and documentation

Define important variables close to their source:

.env-fuse
#@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:

.env-fuse
#@import services/auth.env prefix=AUTH_
#@import services/database.env prefix=DB_

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:

mv .env .env-fuse
eval "$(dotenv-fusion load -f .env-fuse)"

If a tool still needs a plain .env file, generate it from .env-fuse instead of maintaining both files by hand:

dotenv-fusion fuse -f .env-fuse -o .env

From python-dotenv, keep the call site explicit and point it to .env-fuse:

from dotenv_fusion import load_dotenv

config = load_dotenv(".env-fuse")

Performance

  • Keep import trees readable; avoid many tiny files for values that always change together.
  • Use dotenv-fusion load directly when the process can receive exported variables.
  • Generate .env only for tools that need a file, and cache or reuse it inside the same job when multiple commands need the same resolved environment.