dotvault:以一次 pull 在手提電腦、CI 和雲端開發環境使用同一份 .env
One dotvault pull, the same .env everywhere: laptop, CI, cloud dev environments
dotvault 透過 dotvault pull,讓手提電腦、CI 及雲端開發環境按同一方式取得同一專案的 .env 變數。每個環境只需安裝、登入及初始化一次;CI、伺服器和自行運作的 AI Agent 則使用服務 token。使用 dotvault run -- 可略過 .env 檔案,直接把變數傳給指定程序。
This is part 2 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.
A .env rarely lives in one place. The same variables are needed in CI, on the server that runs the app, and in cloud development environments such as GitHub Codespaces, Codex cloud in ChatGPT and Claude Code on the web. With dotvault, every one of those places gets its values the same way: dotvault pull, one command. Install, login and init happen once per place. After that it is pull, or run if you want no .env file.
Any environment that has Node.js and can reach the internet over HTTPS runs the same steps. The terminal screens in this part come from a fresh node:20 Docker container with nothing carried over from my own machine. This is part 2 of 12, run on October 18, 2026 against the production console at app.dotvault.io and the latest published CLI (0.15.7). Every step has its screen.
The short version
-
One command per place:
dotvault pull. Install, login and init are once per place. If you commit theproject-config.jsonthatinitwrites, a fresh clone does not even needinit. -
Fix a value once, in the console. Every place picks it up on its next pull. With
run, the next start already uses it. - Node.js plus outbound HTTPS is all it needs. This part was done in a fresh Linux container with a keychain (gnome-keyring and libsecret) added. Where there is no OS keychain, the CLI stores the token in a mode-600 file instead.
-
dotvault pullrewrites one marked block inside.envand leaves alone your own lines for keys dotvault does not hold. The file is written with mode 600, so only your user can read it. -
dotvault run -- <command>skips the.env. The values from this part go into that one process without being written to a.env. -
Places where no human logs in (CI, servers, AI agents working on their own) still run the same
dotvault pullorrun. The only difference is a service token instead of a login. Parts 7 and 8.
Where the same .env has to exist
The values live in one dotvault project. Only the way each place receives them differs.
| Place | Example | How it gets the values |
|---|---|---|
| Your machine | Mac, Windows, Linux | Log in, then pull or run (this part) |
| Cloud dev environment with a human at the terminal | GitHub Codespaces | Exactly as on your machine (this part) |
| Cloud dev environment where an AI agent works on its own | Codex cloud in ChatGPT, Claude Code on the web | Service token, then the same pull or run (issuing and passing the token: parts 7 and 8) |
| CI/CD | GitHub Actions and the like | Service token, then the same pull or run (part 7) |
| Server runtime | The server or container that runs the app | Service token, then the same pull or run (issuing and passing the token: part 7) |
Once per place, and every time after
| Command | When | What it does |
|---|---|---|
npm i -g @earthlink/dotvault |
Once per place | Installs the CLI |
dotvault login |
Once per place (again only when the refresh token expires; see step 2) | Logs in with email and password |
dotvault init |
Once per project folder (not needed in a clone once committed) | Binds the folder to a project |
dotvault pull / dotvault run -- <command>
|
Every time you need the values | Writes the .env / starts a command with the values |
Creating the project and its keys in the console is also once per project. The rest of this part walks through the first time, in order.
Before you read on
-
Who this is for. Developers, freelancers and small teams who are still keeping the same
.envin several places by hand. You do not need to have read part 1 to finish this one. -
Date and environment. Screens are from October 18, 2026: the production console and CLI 0.15.7, the latest that day.
npm i -gnow installs 0.15.8; on October 23 I ran the same steps on 0.15.8, and the differences are the version number, the marker line written into.env(# BEGIN: dotvault (managed by dotvault)), the wording in brackets of the network error, and that a hand-written line for a key pull also writes is now commented out (see step 5), within what the steps of this part show (key and file counts are higher now because later parts add keys, files and Global Keys). The terminal is a Linux container (Dockernode:20) with no earlier configuration and a keychain (gnome-keyring and libsecret) added. On macOS the commands are the same (the screens in this part were not taken on macOS; the first login may show a dialog asking to allow Keychain access). -
Terminal screens are recomposed. Raw terminal screenshots come out too small to read, so the terminal figures reproduce the actual output verbatim in a cleaner frame. The raw output is kept as is. Exit codes, recorded separately, are shown as an
echo $?line. - Demo account. The email address, project name and values in the screens were created for this series. None of the values are real.
-
Vocabulary. "Secret" means any value that must not leak: API keys, passwords, connection strings. A
.envfile is those secrets asKEY=VALUElines. Part 1 has the full glossary.
1. Install (once per place)
Node.js 20 or later is the only requirement, on your machine or in a cloud dev environment's terminal.
npm i -g @earthlink/dotvault
dotvault --version
npm prints one deprecation warning for a dependency. The install still completes. If dotvault --version prints a version number, you are ready.
2. Log in (once per place)
dotvault login asks for Email, then Password. The password is not echoed.
dotvault login
dotvault whoami
. Token expires in 3600s. Refresh token saved."; `dotvault whoami` prints the email, the token expiry and "org: org_personal_… Personal (personal, org_admin)"."/>
The token goes into the OS keychain (Keychain on macOS), not into a plain-text file. "Token expires in 3600s" sounds short, but a refresh token is saved next to it, so you are not logging in every hour. The refresh token lasts 30 days; when it runs out, run dotvault login again.
dotvault whoami shows your email and the organisation you are in. At this point a Personal organisation already exists for you; it was created on your first login. Nobody had to set it up.
3. Create a project and three keys in the console (once per project)
The contents of the .env are prepared in the browser console at app.dotvault.io, with the same email and password. In a Personal organisation the sidebar shows eight items under a PERSONAL heading. The team features (Teams, My Organizations) are not there.
Open Projects and press New project. The modal asks for one thing, a name: lowercase letters, digits, hyphens and underscores, starting with a letter or digit. I used demo-app. Project names are unique across dotvault, so demo-app is taken; pick your own.
After Create, the list shows demo-app with three environments already attached: development, staging, production. Part 3 is about those. This part only uses development.
Open demo-app. The Keys tab has development selected and no keys yet, so it says "No keys in development yet." The Selected key panel on the right says "Click a key on the left to inspect."
Press + Add key and an inline row appears with Key name and Value. Fill both, press Add. Three times for DATABASE_URL, API_BASE_URL and SMTP_PASSWORD.
Click a key name and the Selected key panel shows its path (development/env/DATABASE_URL), a description field, a reveal button, delete, and a button that moves the key to Common, a shared place for keys that every environment can use. Common is covered in part 3. Values are not shown in the list. They appear only when you press the reveal button. The button says "audit-logged"; what the audit record actually holds is checked in part 10.
That is all the console work. When a value changes later, this is where you change it. Back to the terminal.
4. Bind the folder to the project (init, once)
In the project folder, dotvault init writes one file, project-config.json. The project name defaults to the folder name; dotvault init --name <name> overrides it.
cd demo-app
dotvault init
cat project-config.json
There are no secrets in this file: the project name, the API URL, the list of files to write, the default environment. Commit it. Any clone, in CI or in a cloud dev environment, can then pull without running init.
5. Pull the .env (every time)
dotvault pull runs three stages on one progress bar, authenticating, fetching, writing, and finishes with the number of keys written.
Before pulling, I put a few hand-written lines in .env (a comment, PORT and DEBUG) to show what happens to them.
dotvault pull
cat .env
ls -l .env
Three things to notice.
-
Your lines survive. dotvault only writes the block between
# BEGIN:and# END:. The next pull replaces that block. That is what "Markers preserved." means. On 0.15.8, the current version, a line outside the block for a key that pull also writes (say a hand-keptDATABASE_URL=) is turned into a comment starting with# dotvault: moved into the managed block below:, and pull prints a warning. Nothing is deleted; the value inside the block is the one that applies. When you move a hand-kept.envto dotvault, that warning tells you which keys overlapped. -
The file is mode 600.
-rw-------means your user can read and write it and no other user on the machine can read it. - The keys added in this part come out sorted by name, not in the order you added them in the console.
After you change a value in the console, the same dotvault pull in any place replaces the marked block with the current values.
6. Run without a .env (run, every time)
If you would rather not have a .env on disk, dotvault run -- <command> puts the variables from this part into that one process's environment without writing a .env. One exception: if the project holds whole-file secrets (such as .npmrc, covered in part 5), run writes those files to disk while the command runs. They are removed when the command ends normally, but can be left behind if you stop dotvault itself, for example with Ctrl-C. Part 5 covers this.
dotvault run -- printenv DATABASE_URL
dotvault run -- sh -c 'echo "$API_BASE_URL"'
When to use which:
dotvault pull |
dotvault run -- <command> |
|
|---|---|---|
.env on disk |
Yes (the marked block only) | No |
| Who sees the values | Anything that reads the file | That one process |
| Fits | Tools and frameworks that expect a .env, start scripts written by other people |
Local development (dotvault run -- npm run dev), CI, anywhere you do not want a plain-text .env on disk (whole-file secrets: part 5) |
| After a value changes | Pull again | The next start uses the new value |
For local development, use run. If there is no plain-text .env, it cannot be committed by accident and it does not end up in a backup.
It works without init too
In a folder with no project-config.json, pull and run still work if you name the project. A bare dotvault list shows the projects you can read.
dotvault list demo-app
dotvault pull demo-app
Putting the project name straight after the command (dotvault pull demo-app) and --project demo-app mean the same thing. init is only for pinning a folder to a project.
Reading a failure
A failure prints ✖, a code in capitals, and sets a fixed exit code (echo $?). Scripts can branch on the exit code. The table lists the causes seen in this part and what to check first; the same code can have other causes too.
| code | exit | What happened | What to do |
|---|---|---|---|
AUTH_REQUIRED |
3 | Not logged in | dotvault login |
PERMISSION_DENIED |
4 | No permission on that project or environment, or no project by that name | First check the project name for typos: a project name that does not exist also returns this code. If the name is right, ask an org admin to grant access in the console. A service token also gets it when it reads an environment or project it was not allowed (part 7). Permissions are covered in part 9 |
PROJECT_NOT_FOUND |
5 | No secrets for that project and environment (here: staging, which had no keys yet) | Check your project's name with dotvault list, and in the console whether that environment has keys |
VALIDATION_ERROR |
6 | No project-config.json, or something missing from the command |
Name the project, or run dotvault init in that folder |
NETWORK_ERROR |
2 | The API URL cannot be reached | Check the connection and apiUrl in project-config.json
|
BAD_PROJECT / BAD_ENV
|
1 | Project name in the wrong format (capitals), or an environment the project does not have | Use a lowercase name; use development, staging, production, or an environment you add in part 3 |
PROJECT_NOT_FOUND also appears when the project exists but that environment has no keys at all. The second failure in the screen is exactly that: staging, with nothing in it yet.
Same steps on Linux and in containers without a keychain
Where the OS keychain cannot be used (Linux without libsecret, or a headless container where libsecret is installed but no keychain service is running), the CLI prints a two-line notice and stores the token in ~/.config/dotvault/credentials.json with mode 600. The commands and the behaviour are the same. The screen below is the same container run without its keychain.
Appendix: your account
Forgotten password
From "Forgot your password?" on the login screen, enter your email to receive a confirmation code, then enter the code and the new password on the next screen. Passwords need at least 12 characters, with upper and lower case, a digit and a symbol. The form says 8, but the server rule, checked on October 23, 2026, is 12.
Display language
User Settings in the sidebar has a Language select. Choosing one saves immediately and shows "Saved." The setting lives in the browser, not on the account, so another browser starts from its own default.
Logging out
dotvault logout removes the stored token of the account currently in use from this machine (the keychain, or the file where there is no keychain). If no other account is logged in on this machine, dotvault whoami then says "Not logged in." If you also logged in with another account, that one is used after logout, so log out of it as well.
."; `dotvault whoami` prints "Not logged in." with exit code 3."/>
Not in this part
Registering and removing multi-factor authentication will be added to this part's appendix once the console has those screens. Switching to a team organisation after an invitation is covered, with its screens, in part 9.
Next
Part 3: "Three environments plus one shared layer, out of the box: how a dotvault project is shaped." The three environments that came with the project, the Common layer, comparing environments, and archiving and deleting a project, 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.
來源:DEV Community · dev.to















