Advanced

Environment Inheritance

Learn how secrets cascade through the project, target, and environment hierarchy.

Back to all guides

The dotEnv Cloud hierarchy is more than organization. Secrets defined higher up cascade down to the levels below, and more specific levels can override what their parents define. Understanding this inheritance is what lets you keep shared configuration in one place and only specify what differs per environment.

The cascade

Recall the four levels: organization → project → target → environment. When you pull an environment, the CLI merges secrets from every level above it:

how merging works
project (my-app) LOG_LEVEL=info API_URL=https://api.example.com
  target (production) LOG_LEVEL=warn
    environment (api) PORT=8080
# Pulling my-app/production/api yields:
LOG_LEVEL=warn # overridden at the target level
API_URL=https://api.example.com # inherited from the project
PORT=8080 # defined at the environment level

The rule is simple: the most specific level wins. A value set at the environment level overrides the same key set at the target or project level, and a target value overrides the project value.

Pulling with inheritance (the default)

By default, pull merges all levels, which is almost always what you want at runtime:

terminal
dotenv pull my-app/production/api

Inspecting a single level

To see only what is defined at one level (without anything inherited), pass --level-only. This is invaluable when you are debugging where a value actually comes from:

terminal
# Only the secrets stored directly on the environment
dotenv pull my-app/production/api --level-only
# Compare with the project level
dotenv pull my-app --level-only

Pushing to the right level

Where you push determines who inherits a value. Push shared defaults to the project so every target and environment receives them, and push overrides to the specific level that needs them:

terminal
# Shared across the whole project
dotenv push my-app .env.defaults
# Override just for production
dotenv push my-app/production .env.production
# Specific to one environment
dotenv push my-app/production/api .env.api

You can even push all three at once with the per-level flags:

terminal
dotenv push my-app --project=.env.project --target=.env.target --env=.env.env

A practical pattern

A common, clean layout is:

  • Project level: values that are identical everywhere (feature flags, public URLs, log format).
  • Target level: tier-wide differences (production vs staging database hosts, log levels).
  • Environment level: values unique to one runtime (the port a service binds to, a worker concurrency setting).

This keeps duplication to a minimum: change a shared value once at the project level and every environment picks it up. To make values reference each other, continue with Variable Templating.