Stop editing the same value in every .env: one dotvault project for every environment, one Common layer
This is part 3 of the series "dotvault, Start to Finish: One Command from Your Laptop to CI, Your Team, and Your AI Agents." dotvault will be released free of charge when this series ends. In this part, a key disappears from development's key list, and yet the next development pull still writes it i

nto .env. Nothing went wrong. That key now lives in Common, the layer dotvault merges into the pull of every environment, and it is stored there exactly once. That is the point of this part.
If you keep a separate .env for development, staging and production, every value that is the same everywhere is written once per environment, and changing it means editing it in every file. Miss one, and that environment runs on the old value. In dotvault, those separate files become the development, staging and production environments of one project, managed in one place in the console. A value shared by every environment goes into Common once, so you change it once.
When one environment needs something different, a key of the same name in that environment wins over Common. This is part 3 of 12, continuing with the demo-app project from part 2. The separate .env files become environments of one project: development, staging and production. You switch between them on the Keys tab and manage every environment's values in one place.
You can add your own environments on the Settings tab (this part adds preview). A value that is the same in every environment goes into Common (_shared), once. Common is merged into every environment's pull, so you never edit the same value in two places. If an environment has a key of the same name, the environment's value wins.
Compare envs… shows the keys one environment has and the other lacks, and copies them in place. Promote to Common turns a key that already sits in your environments into a single Common key, using the value of the environment you selected. The same-named key leaves the standard environments and stays in Common alone. Environments you added yourself keep their own key.
On the CLI, the environment is chosen by -E, then DOTVAULT_ENV (or APP_ENV if that is unset), then defaultEnv in project-config.json. A finished project is archived first, then deleted, in the console. Where we start. Part 2 left demo-app with three keys in development and a successful dotvault pull.
You can follow this part without having read it. Versions and setup. Screens are from the production console at app.dotvault.io and CLI 0.15.7, in the same Node 20 Linux container as part 2. From 0.15.8 on, the .env marker line reads # BEGIN: dotvault (managed by dotvault).
I checked that environment selection (-E, DOTVAULT_ENV, defaultEnv) behaves the same on 0.15.8. Terminal screens are recomposed. The actual output is reproduced verbatim in a cleaner frame. The raw output is kept as is.
Exit codes, recorded separately, are shown as an echo $? line. Demo account. Project names, key names and values were created for this series.
None of the values are real. Vocabulary. "Secret" means any value that must not leak, such as an API key or a password. An "environment" is the unit that lets the same key hold a different value, such as development, staging or production.
A "key" is the name half of a NAME=value line in .env. In dotvault, the .env you kept per environment becomes an environment inside one project. The demo-app project from part 2 has three: development, staging and production. They are dotvault's standard environments and cannot be removed.
The Keys tab has an environment switch above the list; pick an environment and you see its keys. Every environment's values are read and edited from this one screen. This screen already shows preview, which is added later in this part, and Common (_shared), where values shared by every environment go. We build up to it.
With every environment in one project, you can put two of them side by side. After part 2, the three keys exist only in development. Whether staging or production need the same keys is not visible until you compare. "⇄ Compare envs…" on the Keys tab opens a drawer with two environment selectors.
"Only in development" lists the keys the other side lacks. The "Copy to production" button on a row copies that one key; "Copy all to production" copies every key in the list. For this article I first copied all three to staging. Comparing staging with development afterwards shows a single line.
These two screens were taken after section 3 moved API_BASE_URL to Common. Keys in Common are not part of the comparison, so only two keys (DATABASE_URL and SMTP_PASSWORD) appear. What gets copied is the key name and the value at that moment. Changing the development value later does not propagate to staging.
That is the point of environments, so it is intended. A key that should stay identical everywhere is not copied around; it goes into Common, next. In this demo, API_BASE_URL holds the same value in every environment. In a real project, this is the place for keys like a third-party service's public key or the address your app sends email from.
Writing it into each environment means changing it in each environment. Common (shown as Common (_shared) in the console, _shared on the CLI) is the layer for that. A key placed in Common is written out with every environment's pull. There is one value, so a change is one edit.
A key already sitting in an environment moves into Common with "⬆ Promote to Common (_shared)". Select the key and press it; a confirmation appears. As the dialog says, development's value becomes the Common value. The same-named key leaves the three standard environments (development, staging, production) and lives in Common alone.
Keys in environments you added yourself stay, and they still take priority over Common. After confirming, development and Common look like this. That is the key that vanished from development's list at the top of this article. The pulled .env still contains it, now as the Common value (checked in section 5).
When one environment needs a different value, add a key of the same name in that environment. The environment's value takes precedence over Common. Section 5 checks that too, with a different value in preview. Common gathers the values shared by the environments of one project.
Values shared across projects come in part 6, with Global Keys. When the standard three are not enough, add another environment to the same project; it sits next to the others on the same screens. Add it under Environments on the Settings tab. Names are lowercase letters, digits and hyphens, starting with a letter, at most 32 characters.
"Copy keys from an existing env" copies that environment's keys into the new one right away. I added preview, copied from development. Environments you added get Rename and Delete. The standard three and _shared do not.
Rename opens a dialog for the new name and notes that every key and permission referencing the old name is rewritten. An environment that still holds keys or permissions cannot be deleted. Pressing Delete on preview with keys in it, and confirming, shows "This environment still has keys or permissions. Remove those first." and nothing is removed.
To delete it, first remove its keys and any permissions granted on that environment. After this I added API_BASE_URL to preview with a different value (https://preview.example.com/v1), to see which wins, Common or the environment. The values kept in one place in the console come out on the terminal by naming the environment. dotvault list demo-app prints the key count per environment, including the preview you added.
production shows 1 although nothing was put there. That is Common being counted: API_BASE_URL in Common is merged into every environment. -E (--env) selects the environment for a pull. Without it, and without the environment variables below, defaultEnv from project-config.json applies (development, as written by init in part 2).
dotvault pull dotvault pull -E preview dotvault pull -E production Three things to notice. The development pull contains API_BASE_URL. It left development's list, but it comes back as the Common value. One value in Common reaches every environment's .env.
The preview pull contains preview's API_BASE_URL. A same-named key in the environment beats Common. The written .env does not record which environment it came from. The result line always shows env=….
Before starting work, pull again for the environment you need. DOTVAULT_ENV selects the environment too, for cases like CI or npm scripts where you want to switch without editing the command. When both are present, -E wins. If DOTVAULT_ENV is unset, the CLI also reads APP_ENV as the environment.
So if your app already uses APP_ENV for its own settings, a plain pull with neither -E nor DOTVAULT_ENV gets that environment, not defaultEnv. DOTVAULT_ENV is the reliable way to pick the dotvault environment. So the order is -E, then DOTVAULT_ENV (or APP_ENV), then defaultEnv. defaultEnv applies only when -E and both variables are absent.
The same screen ends with a second dotvault init: in a folder that already has project-config.json it stops instead of overwriting, and --force is the way to start over. How Written as Fits Command flag dotvault pull -E staging A quick look at another environment Environment variable DOTVAULT_ENV=staging dotvault pull CI and start scripts, switching without changing the command Config file defaultEnv in project-config.json The folder's everyday environment; init writes development defaultEnv takes one of development, staging or production. Pick an environment you added yourself, such as preview, with -E (or DOTVAULT_ENV) each time. A project that holds all your environments is also put away as a whole.
The Danger zone sits at the bottom of the Settings tab. Archive this project asks for confirmation and then removes the project from the console's list. Archiving is reversible. Deleting is only possible after archiving.
Set the list filter to "Archived only" and the archived project appears with Unarchive and Delete… buttons. Delete… opens a dialog that asks you to type the project name. Delete permanently stays disabled until the name matches. What goes is not only the current values but the whole version history covered in part 4.
This screen was taken on a throwaway project, scratch-app, so that demo-app survives. After Delete permanently, the project is gone even under "All (incl. archived)". demo-app was unarchived and continues into part 4.
Archiving and deleting a project are done in the console. On the CLI, dotvault project offers create and list. The .env you used to keep per environment becomes an environment of one dotvault project, managed in one place in the console. A value every environment shares goes into Common once and reaches every pull, so you never edit the same value in two places.
Part 4: "Every value keeps every version: writing, pulling and rolling back keys." Keys from both the console and the CLI, reading values out, and restoring an earlier version from the version list, one screen at a time. dotvault stores the secrets in your .env and config files server-side and hands them to any authorised developer, CI job or AI agent with a single command (https://dotvault.io). It will be released free of charge when this series ends, and the steps in these articles are the steps you will use on day one. If you find this useful, please like and follow.
Our products are listed at https://www.eln.ne.jp/products EarthLink Network builds its own products to run the whole company on AI — each one born from a need along the way. See the full index of what we build: → The 18 products EarthLink Network builds in-house For more about the company and each product, visit www.eln.ne.jp.
Key Takeaways
- •Nothing went wrong.
- •This story was reported by Dev.to, covering developments in the dev space.
- •AI advancements continue to reshape industries — read the full article on Dev.to for complete coverage.
📖 Continue reading the full article:
Read Full Article on Dev.to →

