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:
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:
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:
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:
You can even push all three at once with the per-level flags:
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.