CLI reference
Every turboenv command, option and environment variable, and the rules for how they combine.
npm install -g turboenv-cli
Node 24 or newer. You can also run it without installing, which is what CI usually wants:
npx turboenv-cli run -- npm start
Commands
turboenv login
Signs this machine in to your team through a browser. Writes a credential to
~/.turboenv/credentials.json with mode 0600.
turboenv login
turboenv login --no-browser # print the URL instead of opening it
Your terminal prints a short confirmation code that you type into the approval page. If a page ever asks you to approve a login and your terminal is not showing a code, something else started that request — deny it.
The credential covers every project your team owns, so -p and -e become
required unless a turboenv.json supplies them. See
Tokens and access.
turboenv init
Writes a turboenv.json holding the project and environment this directory
works against, so the other commands need no flags.
turboenv init # pick from a list
turboenv init -p checkout-service -e development # non-interactive
turboenv init --force # overwrite an existing file
With no flags it lists your team's projects, then that project's environments, and asks you to choose. Both are checked against the server either way, so a typo fails now rather than at your next deploy.
Commit the file — see Project configuration.
turboenv pull
Safely merges a stored snapshot into a local .env.
turboenv pull # show the diff, then confirm
turboenv pull -f .env.production # write a different file
turboenv pull --version 12 # pull an immutable older snapshot
turboenv pull --prune # make an exact remote copy
Remote values replace matching local values; local-only variables remain unless
you pass --prune. Existing files are backed up to <file>.bak, then replaced
atomically with mode 0600. Formatting and comments are rewritten. Project
tokens can pull because they are read-only credentials.
turboenv push
Pushes a local .env up as a new version.
turboenv push # show the diff, then confirm
turboenv push -f .env.production # read a different file
turboenv push --prune # also remove what the file omits
turboenv push --yes -m "rotate key" # no prompt, for scripts
Requires a turboenv login credential; project tokens are read-only. Full
behaviour in Project configuration. turboenv sync remains a
deprecated alias for push.
turboenv run -- <command>
Fetches variables, injects them into the environment, and runs your command.
turboenv run -- node server.js
turboenv run -p checkout-service -e production -- ./bin/api
turboenv run --version 12 -- python manage.py runserver
Everything after -- is your command, untouched, so its own flags cannot be
confused with turboEnv's.
turboenv print
Writes the variables to stdout in .env format, with values quoted so the
output is safe to source.
turboenv print > .env
turboenv print -p checkout-service -e development
This puts decrypted values on disk. Prefer run unless something genuinely
needs a file.
turboenv delete
Deletes a project, its environments, every version and variable in them, and every token issued for it.
turboenv delete # summarise, then type the slug to confirm
turboenv delete -p checkout-service # name the project rather than using turboenv.json
turboenv delete -p checkout-service --yes # no prompt, for scripts
Confirming means typing the project's slug, not pressing y. There is no
version to roll back to afterwards and no undo, so the prompt asks you to spell
out what you are destroying.
--yes only works alongside an explicit -p. Otherwise turboenv delete --yes
run in the wrong directory would silently destroy whichever project the nearest
turboenv.json happens to point at.
Requires a turboenv login credential; project tokens cannot delete anything.
Anything deploying with a token from the deleted project stops being able to
read its configuration immediately.
Customer-managed projects
Configure the separately held customer key before reading, pulling or pushing:
turboenv key generate --key-file .turboenv-key
export TURBOENV_KEY_FILE="$PWD/.turboenv-key"
turboenv run -- npm start
Use a deployment secret mount rather than committing the file. TURBOENV_KEY
also works. The CLI unwraps and decrypts locally, caches only ciphertext for
these projects, and removes both key variables before starting the child.
There is no recovery if every copy of the customer key is lost.
Options
| Option | Effect |
|---|---|
-p, --project <slug> |
Which project to read |
-e, --environment <name> |
Which environment to read |
--version <n> |
Pin a version instead of taking the latest |
--token <token> |
Override TURBOENV_TOKEN |
--url <url> |
Override TURBOENV_URL, for a self-hosted instance |
--override |
Let stored values replace ones already in the environment |
--no-cache |
Do not read or write the local cache |
--no-browser |
With login, print the URL instead of opening it |
-f, --file <path> |
With pull/push, which file to use (default .env) |
-m, --message <text> |
With push, why this changed |
--prune |
With pull/push, remove variables missing at the source |
--yes |
With pull/push, skip confirmation. With delete, skip it too, but only alongside an explicit -p |
--force |
With init, overwrite an existing turboenv.json |
Whether -p and -e are required depends on your credential and whether a
turboenv.json is present. A project token already knows its scope, so they are
optional — and naming a different project is refused rather than quietly
ignored. A login credential has no implicit target, so both are required unless
the config file supplies them.
Which project and environment win
Most explicit first:
-pand--environmentflagsTURBOENV_PROJECTandTURBOENV_ENVIRONMENTturboenv.json, from this directory or the nearest one above it- The token's own scope, for a project token
The two are resolved independently, so a turboenv.json naming a project can be
combined with -e staging for one command.
Environment variables
| Variable | Meaning |
|---|---|
TURBOENV_TOKEN |
A project token from the dashboard |
TURBOENV_URL |
API base URL, for a self-hosted instance |
TURBOENV_PROJECT |
Default project, overriding turboenv.json |
TURBOENV_ENVIRONMENT |
Default environment, overriding turboenv.json |
Which credential wins
Most explicit first:
--tokenTURBOENV_TOKEN- A stored
turboenv logincredential
A stored login is refused when CI is detected, whatever the file says. The
CLI looks for CI, GITHUB_ACTIONS, GITLAB_CI, CIRCLECI, BUILDKITE,
JENKINS_URL, TEAMCITY_VERSION, TF_BUILD, BUILD_NUMBER and
CONTINUOUS_INTEGRATION. A value of false or 0 is believed, since some
providers set CI=false deliberately.
Existing variables are not overwritten
If a variable is already set in the environment, the stored value does not replace it. A value you exported in your shell, or one your orchestrator injected, always wins.
PORT=4000 turboenv run -- node server.js # PORT stays 4000
That default makes local debugging predictable — you can override one value for
one run without editing anything stored. Pass --override to invert it, which
is occasionally what you want when a base image sets a variable you need to
replace.
Caching
The last successful response is written to ~/.turboenv/cache with mode 0600,
keyed by a hash of the credential, project, environment and version. If the API
is unreachable at startup, the cache is used and a warning goes to stderr, so an
outage does not stop your app from booting.
The cache holds plaintext values, which is why it is 0600 and why the key
includes the credential: two people on one machine do not share a cache entry,
and neither do two projects.
--no-cache disables reading and writing it.
Self-hosted instances
Point the CLI at your own deployment with --url or TURBOENV_URL:
export TURBOENV_URL=https://turboenv.internal.example.com/api
turboenv login
Credentials are stored per base URL, so you can be signed in to your own instance and the hosted one at the same time without one replacing the other.
Exit codes
turboenv run exits with the exact status of the command it ran, and forwards
SIGINT, SIGTERM and SIGHUP to it. A failure inside turboEnv itself — no
credential, unknown project, unreachable API with no cache — exits 1 with a
message on stderr.