turboEnv

Project configuration

Commit a turboenv.json so nobody has to remember flags, then safely pull and push dotenv files.

Every command so far has needed -p and -e. Committing a small file removes them, and gives your repository one obvious answer to "which environment does this project use".

turboenv init

turboenv init

With no flags it lists the projects your team owns, asks you to pick one, then does the same for its environments. It writes:

{
  "project": "checkout-service",
  "environment": "development",
  "encryption": "customer"
}

Commit it. It holds names, never values — there is nothing sensitive in it, and checking it in is what makes a fresh clone work without instructions.

encryption is written by turboenv init as a fail-closed expectation. It is public metadata, never key material. The file cannot set the API host: a committed file is editable by anyone who can land a commit or hand you a repository, and a url in it would point the CLI at their server with your token attached. The CLI refuses a config containing one. If you run your own instance, set TURBOENV_URL in your environment instead, and the CLI will print which host it is using whenever it is not the hosted one.

From then on the flags are optional, from that directory or any beneath it:

turboenv run -- npm start
turboenv print

If you already know the names, skip the questions:

turboenv init -p checkout-service -e development

Both are checked against the server either way. A misspelled environment fails here, in front of the person who can fix it, rather than during a deploy.

An existing turboenv.json is never overwritten without --force.

Which one is used

Most explicit first:

  1. -p and -e
  2. TURBOENV_PROJECT and TURBOENV_ENVIRONMENT
  3. turboenv.json
  4. The token's own scope, for a project token

The two resolve independently, so a committed project plus -e staging for one command works as you would expect.

The file is found by walking up from the working directory, like package.json or .git. In a monorepo, put one in each service directory and each gets its own defaults.

Environments differ per developer

turboenv.json is shared, so it should name the environment most people want — usually development. Anyone who needs another one overrides it for a single command with -e, or for a whole shell:

export TURBOENV_ENVIRONMENT=staging

turboenv pull

pull merges a stored snapshot into .env:

turboenv pull

Remote values replace matching local values. Variables found only in the local file remain, so machine-specific settings are not accidentally erased. Pass --prune when the file should be an exact copy of the stored snapshot, or --version 7 to restore an older version locally.

Before writing, the CLI shows a key-only diff and asks for confirmation. An existing file is copied to .env.bak; the replacement is written atomically with mode 0600. Comments and formatting are rewritten, and a malformed line is refused rather than silently dropped.

turboenv push

Until now variables have gone in through the dashboard. push uploads a local .env up instead, which suits the case the dashboard is worst at: you already have a working file and want it stored.

turboenv push

It shows what would change and waits:

checkout-service/development is at version 7
Reading /home/you/checkout/.env

  + STRIPE_WEBHOOK_SECRET
  ~ DATABASE_URL

Save as version 8? [y/N]

+ is new, ~ is a changed value, - is a removal. Values are never printed, only names — a diff is the sort of thing that ends up pasted into a chat, and the keys are enough to see what you are about to do.

Confirming writes a new version. Nothing is edited in place, so the previous version stays readable and a mistake is undone by pinning it:

turboenv run --version 7 -- npm start

Only a person can push

push needs a credential from turboenv login. A project token is read-only and gets a 403.

This is deliberate. Tokens live in CI, in container images and in agent configuration — the places most likely to leak. Keeping them read-only means a leaked token exposes configuration without letting anyone change what your next deployment reads. It also means every version has a name against it, which is what makes the history worth reading.

Removing things needs --prune

A variable on the server that your file does not mention is kept, and the CLI says so:

Already up to date. Nothing to push.
1 variable exists only on the server; pass --prune to remove it.

That default exists because the common reason for a missing key is a stale local file, not an intended deletion. When you do mean it:

turboenv push --prune

Concurrent saves are refused

push sends the version it built its diff from. If a teammate saved in between, you get:

This environment is now at version 9, not 8. Somebody else saved while you
were working. Re-run to see the new diff.

Nothing is written. Re-run, look at the new diff, and decide again — which is the point: without this check the later of two pushes would silently erase the earlier one and both people would be told it worked.

Secret flags survive

A variable already marked non-secret stays non-secret. New keys are created as secrets, so a value is protected unless you have said otherwise in the dashboard.

In scripts

push refuses to run unprompted, since it changes stored configuration. Pass --yes when there is no terminal:

turboenv push --yes -m "import from .env.production"

-m records why the version changed and shows up in the history. Worth setting whenever the diff is not self-explanatory.

Other options:

turboenv push -f .env.production   # read a different file

Pushing production always asks you to type the environment name, even interactively. Pressing y by reflex is easy; typing production is not.

What it will not do

A file with no variables is refused rather than emptying the environment, since an accidentally truncated .env is far more likely than a genuine wish to delete everything. Lines it cannot parse are reported on stderr rather than skipped silently, because a variable quietly dropped here is one missing at startup for no visible reason.

turboenv sync remains available as a deprecated compatibility alias for turboenv push.