Skip to content
Get early access

Coming from a .env file

If you already have a working .env, this is the fastest path in.

Terminal window
cendarum import --env dev --file .env
cendarum: imported 12 keys into dev (version 2)

You can also pipe it, which is useful when the file is generated:

Terminal window
cat .env.example | cendarum import --env dev

With no --file and nothing piped, import reads from stdin.

The dialect is the common one, and deliberately forgiving:

Terminal window
# comments are ignored
export API_URL=https://api.example.test # `export ` prefixes are tolerated
DB_PASSWORD="s3cr3t-smoke-value" # double quotes
FEATURE_FLAG='on' # single quotes, no expansion
MULTILINE="line one\nline two" # escapes inside double quotes

Blank lines are skipped. Surrounding whitespace is trimmed.

$ printf 'DUP=1\nDUP=2\n' | cendarum import --env dev
cendarum: error: duplicate key DUP (lines 1 and 2)

Nothing is imported. This is a deliberate choice: a .env with the same key twice is a file whose meaning depends on your loader’s precedence rules, and silently picking one is how a staging value ends up in production. You get told, with both line numbers, and you decide.

An import is an upsert. For a key that already exists, the value is replaced; its type and note are kept. So if you previously marked DATABASE_URL as a url and wrote a note explaining which replica it points at, re-importing a fresh .env updates the value without discarding that work.

If someone else changed the same environment while you were importing, the import stops rather than guessing:

cendarum: error: import conflicts with concurrent changes — resolve in the desktop app:
cendarum: conflicting key: API_URL

Only key names are shown, never values. Resolve it in the app, where you can see both sides — see sync and conflicts.

Terminal window
rm .env

This is the part people skip. The file was the problem: it sits in your working tree, it gets copied to other machines, and it ends up in a chat message. Once the values are in Cendarum, cendarum run supplies them at launch and there is nothing on disk to leak.

If a tool genuinely cannot read its environment and insists on a file, there’s cendarum export — but read that page before you reach for it, because it exists as a deliberate escape hatch rather than a convenience.

Cendarum sets exactly the variable names you give it, so framework conventions keep working unchanged:

  • Vite exposes only VITE_-prefixed variables to browser code. Name it VITE_API_URL and it reaches the client; name it DATABASE_URL and it correctly stays server-side.
  • Next.js uses NEXT_PUBLIC_ the same way.
  • Create React App uses REACT_APP_.

These prefixes are a security feature of those frameworks, and they are none of our business — we don’t rename anything. See recipes for per-stack invocations.