env0 <command> --help to print the flags of the version you have installed. To install the CLI and run a first deployment, see env zero CLI.
Global flags and environment variables
Every command accepts these flags:
The CLI also reads these environment variables:
Each value resolves in this order: flag, then environment variable, then stored context, then default. In CI and agents, set the environment variables so each run is explicit and does not depend on stored state.
Anywhere a command takes an organization (
--organization, ENV0_ORGANIZATION_ID, or the argument of organization use and organization get), you can pass the organization’s name instead of its id. The name must match exactly one organization your credentials can see, ignoring case and extra spaces. Several matches exit 2 and list their ids, no match exits 4, and a name made only of spaces exits 64. A value shaped like a UUID is always treated as an id. A name costs one extra request to list your organizations, so scripts should pass ids.
Sign-in and stored context
env0 login stores credentials and a default host, organization, and project, so later commands need no flags.
- Browser sign-in. With no API key set and a terminal attached,
loginopens a browser sign-in and asks you to confirm a short code.--no-browserprints the URL instead. - API key. When
--api-key-idand--api-key-secret(orENV0_API_KEY_IDandENV0_API_KEY_SECRET) are set,loginvalidates and stores that key. - No key and no terminal.
loginexits3. CI and agents should set the API key environment variables and skiplogin.
login then asks you to pick an organization and project, and the --organization and --project flags skip the matching picker. Without a terminal, it stores the --organization and --project you pass.
Credentials are stored in the OS keychain. If the keychain write fails, the CLI writes them to a plain-text file readable only by your user, prints a warning: line on stderr naming the file, and still exits 0. env0 logout removes that file.
Credentials are scoped to the API host they were issued for. Signing in to a second host does not sign you out of the first, and a host you have not signed in to exits 3 even when another host is authenticated. The host, organization, and project are stored once, in config.json, so switching hosts means running login again.
Browser sign-in works out which env zero tenant to use from the API host. For a host outside env0.com, it exits 64 unless you pass --tenant. API keys work against any host.
Output
Every command prints one JSON envelope, exceptenv0 skill show (raw Markdown), env0 deployment logs --follow (a log stream), and env0 api (the raw response body). Data goes to stdout and errors go to stderr:
-o jsonprints indented JSON.-o compactprints the same JSON on one line, forjq.-o tableprints aligned columns.- On error, stdout is empty and the exit code is non-zero, so
env0 ... -o compact | jq .datais safe. - Arrays are always arrays. An empty result is
[], nevernullor a missing key. error.hostnames the host the CLI was pointed at. It is absent forusageerrors and for failures before a host is chosen. Credentials and paths are removed from it.
Pagination
environment list, deployment list, and plan list return at most 100 rows by default and add a page object next to data:
"truncated": true means more rows exist. Pass --all to fetch every page. --limit accepts 1 to 100.
organization list, project list, and template list are not paginated. They return the whole collection and take no --limit or --all.
Exit codes
With
--wait or deployment logs --follow, the exit code reflects the deployment’s final status:
The deployment is printed to stdout in both cases, including
failedCommand and errorMessage when it failed. WAITING_FOR_USER means the deployment needs approval, not that it failed.
context
.data.summary first, then .data.deployments, then deployment logs for the full error.
The output has these keys:
environment: the same objectenvironment getreturns.deployments: recent deployments, newest first.summary: drift status and the latest deployment’s status and type. When that deployment failed (FAILURE,INTERNAL_FAILURE, orTIMEOUT), it also carriesfailedCommand, a truncatederrorMessage, andexitCode. It carriescostEstimationwhen the environment has one.subEnvironments: for a workflow environment, each sub-environment’s alias, plus the revision and status of its latest deployment.inventory: only with--inventory. It holdsresources(the count of cloud resources the environment manages),unmanaged(the count of resources Cloud Compass attributes to the environment that its state does not hold),drifted(the count of drifted resources),resourceTypes(a list with the managed and drifted counts for each resource type), andinsights(CloudQuery insight counts by severity on those resources).resourcesis absent when Cloud Compass cannot read the environment’s managed state.driftedand the drift inresourceTypesare absent when drift detection is off for the environment or Cloud Compass cannot read drift. A Cloud Compass problem never failscontext: with no connection the block is{"connected": false, "reason": "..."}, and on any other error it carries onlyerror.
Organizations
organization list
org is an alias for organization.
organization get
organization use
Projects
project list
project get
project create
The new project does not become your stored project. Pass its id (
.data.id) as --project to later commands, or run env0 project use <project-id>. Requires permission to create projects in the organization.
project archive
project get and project list still return it with isArchived: true. The CLI cannot unarchive a project, and archiving a project cannot be undone.
project use
Parent / Child path, is looked up in the current organization and must match exactly, ignoring case. A full path wins over a nested project’s own name, so web picks a top-level Web before Platform / Web. Archived projects are skipped. Several matches exit 2 and list their ids. No match exits 4 and names similar projects.
Templates
template list
64.
template get
template create
Requires permission to manage templates in the organization.
template assign
environment create --template only deploys templates assigned to the target project.
template delete
template list. template get can still return the record.
Environments
env is an alias for environment. Actions take an environment id, not a name.
environment list
All filters are optional and combine. The list does not include workflow sub-environments. To see them, run
context on the workflow environment.
environment get
tags, as {} when the environment has none.
environment create
--wait, its environmentId is the new environment’s id.
A
--repository environment gets a single-use template that needs no cleanup. When no cloud credential is assigned to the project, pass provider credentials as --secret, for example --secret AWS_ACCESS_KEY_ID=.... An assigned credential keeps keys out of your shell history and process list.
environment deploy
--manifest, --node, and --run-from-here are covered in Deploy workflows with the CLI.
environment plan
type is dryRun, and its planSummary counts the planned changes.
- Pass an environment id or a name. A value shaped like a UUID is treated as an id. A name is looked up as
environment list --namelooks it up, except that archived environments are skipped. - Names are not unique within a project, so the CLI never guesses. No match exits
4. Several matches exit2and list each match’s id and status. An empty name exits64. - An archived environment is refused with exit
2, because a dry run would unarchive it. - The exit code follows the dry run as
--waitdoes:0when it finished,1when the plan failed (readfailedCommandanderrorMessage), and75when--timeoutruns out while the dry run may still be running. - If the environment already has a deployment waiting for approval, the dry run queues behind it. The CLI says so on stderr, even with
--quiet. - A dry run that stops at
WAITING_FOR_USERwhile the command waits has finished its plan. The CLI cancels it so nobody can approve it, prints it with statusCANCELLED, and exits0. - For a workflow environment, the workflow’s own dry run has no steps and no
planSummary. Each sub-environment runs its own dry run. When the workflow’s dry run ends, stderr lists each sub-environment by alias with its status, plan counts, and deployment id. Read a sub-environment’s full plan withdeployment logs.
environment apply
Each entry is a deploy manifest, with the same
revision, variables, and subEnvironments as --manifest, plus these fields:
- An environment is found by its exact name in its project, and case counts. Archived environments are ignored. If none exists,
applycreates it fromtemplate. If one exists,applyredeploys it with the entry’s revision, variables, and sub-environment overrides. Two live environments with the same name exit2. - Most checks run before anything changes. An unknown field, an entry listed twice, a missing
templatefor an environment that does not exist yet, a badttlor tag, or sub-environment overrides for an environment that is not a workflow exit64, and nothing is created or deployed. A wrong sub-environment alias gets the same result only whereapplycan read the workflow’s aliases up front, which is an existing workflow environment that has already read its workflow file. For a new workflow environment, or an existing one that has never read its workflow file because its first deployment failed early or has not reached it,applychecks aliases only after it creates the environment or starts the deployment. A wrong alias there becomes that environment’serrorwith typeusage, the command exits1, and the other environments still deploy. - env zero rejects approval on workflow environments. In a project that requires approval by default, a workflow entry needs
requiresApproval: falseto be created. - Environments run in parallel, each as its own deployment. Inside a workflow,
needs:still orders the sub-environments. - With
--only-failed, an environment is skipped unless it does not exist, has never been deployed, or its latest deployment ended inFAILURE,INTERNAL_FAILURE,TIMEOUT,CANCELLED, orABORTED. In a failed workflow environment, when every failed sub-environment is one sub-environment or downstream of it, only that part runs again, as a--run-from-hererun. Otherwise the whole workflow is redeployed, for example when failures start in two or more sub-environments that do not depend on each other, or when the entry sets a top-levelrevision. --waitdoes not stream logs, because the deployments run in parallel. Follow one withdeployment logs <deployment-id> --follow.
One environment failing does not stop the others. The exit code is
0 when every environment started and, with --wait, none ended in a failed state. It is 1 when any environment has an error or a failed deployment, and the full result is still printed. A --wait that times out is that environment’s error, with type transient.
environment destroy
--yes (or --force) and has no prompt. On an environment that requires approval, the destroy waits at WAITING_FOR_USER like any deployment.
With
--file, destroy takes the same file as apply, so a workspace comes down with the file that brought it up. Entries need no template.
- Environments are found as
applyfinds them, and every lookup runs before anything is destroyed.destroy -fshares the parse-level file validation ofapply, such as an unknown field, an entry listed twice, or a badttlor tag, and its environment lookups, so a file that fails those is rejected here too. It does not requiretemplate, and it does not verify workflow aliases or whether the target supports sub-environment overrides, so a file thatapplyrejects for those with exit64still destroys the environments it names. It still validates the variable names and types in sub-environment overrides, and an invalid type exits64before any request. - Nothing to destroy is a
skip, not an error: an environment that does not exist, one never deployed, one already destroyed, or one that already has an unfinished destroy. A rerun never queues a second destroy. - The destroys run in parallel. A workflow environment is destroyed as a unit, with its sub-environments destroyed in reverse
needs:order. - env zero archives each environment once its destroy succeeds. An environment whose destroy fails or waits for approval stays live. After you fix or approve it, run the same command again to destroy what is left.
--requires-approvalapplies to every destroy. Without it, an entry’srequiresApprovalapplies, then the environment’s own setting.--nodeand--prettywork only with a single environment id, and--waitdoes not stream logs.- The output lists every entry in file order, with
name,projectId,environmentId,action(destroyorskip),deployments, anderror. The exit code is1when any environment has anerroror a failed destroy. A destroy waiting for approval still exits0.
environment archive
destroy first to remove them. See mark an environment as inactive.
environment tag list
{"team": ["eng", "payments"]}. Without one, prints every tag key and value used in the organization. tags is an alias for tag.
environment tag set
team=eng team=payments. Prints the updated environment.
environment tag remove
KEY=VALUE exits 64.
Tag limits and permissions are listed in Environment tags. Tags are visible to anyone who can view the environment, so do not put secrets in them.
environment ttl set
ttl set <environment-id> 2h can bring the end forward. The minimum is 1h. Read the new end from lifespanEndAt in the output: env zero truncates it to the minute.
environment ttl extend
2. extend reads the current end and then writes a new one, so a TTL change someone makes in between is overwritten.
environment ttl clear
lifespanEndAt disappears from the output. For default and maximum TTLs, see Configure policy TTL.
Waiting for a deployment
--wait polls the deployment until it reaches a final status, then exits with the code for that status. It retries 5xx and 429 responses. A deployment that reaches WAITING_FOR_USER stops the wait with exit 0. Approve it with deployment approve.
While it waits, the deployment’s step logs stream to stderr. Stdout still receives only the final deployment, so --wait -o compact | jq works. Logs keep their color on a terminal and lose it when redirected. Secrets are redacted before the logs reach the CLI.
Deployments
deployment list
deployment get
errorMessage, failedCommand, and exitCode for a failed deployment.
deployment logs
--follow, returns the deployment’s status, failedCommand, exitCode, full errorMessage, and steps, with color codes removed. Use it for the root cause of a failure:
stepslists every step in order, each with itsname,status, andevents: the log lines the env zero console shows for that step. A step that has not started has no events. A step withavailable: falsehas no events because env zero no longer stores its logs, which happens for some older deployments.outputholds the deployment’s Terraform or OpenTofu outputs, not the console output, so it is often{}.--step <name>returns only that step. Repeatable. Use anamefromsteps, not adisplayName. An unknown name exits64and lists the valid names.--tail <n>keeps the lastnlines of each step. A step that lost earlier lines hastruncated: true.-o tableprints the fields, then the log lines grouped by step.
--follow (-f), streams the logs to stdout until the deployment finishes, then exits like --wait. --step and --tail do not combine with --follow. The stream is text grouped by step. With -o json or -o compact it is newline-delimited JSON, one {"step", "level", "message", "timestamp"} object per line, with no envelope. --poll-interval defaults to 3s.
deployment approve
WAITING_FOR_USER. The status changes a few seconds later. Approving a deployment that has already moved on exits 2 and is harmless. Approval rights do not approve your own deployments automatically.
deployment cancel
QUEUED deployment, or rejects one at WAITING_FOR_USER.
deployment abort
IN_PROGRESS deployment.
approve, cancel, and abort return right away and take no --wait. To follow a resumed deployment, poll deployment get until its status leaves WAITING_FOR_USER, then run deployment logs <deployment-id> --follow.
Plans
plan list
plan get
planAvailable first: most deployments, including those at WAITING_FOR_USER, store no plan. With no plan, resourceChanges and outputChanges are [], the same as a plan with no changes.
resourceChanges[].actionshowsdeleteandrecreateonly when a plan is stored.- The deployment’s
planSummarycounts can be higher than the entries inresourceChanges. UseresourceChangesfor the actual resources. - To see what drifted, run
plan geton the latest deployment withtypedriftDetection. See Drift detection.
Cost estimation
environment get and list, context (under summary and each deployment), plan get, and deployment get and list include costEstimation: totalMonthlyCost and the deployment’s monthlyCostDiff, as numbers with no currency. The table output assumes USD. The field is left out, which jq reads as null, when cost estimation is off for the organization, the deployment produced no estimate, or the CLI could not read the estimate.
Variables
variable list
var is an alias for variable.
VCS connections
vcs list
id as --vcs-connection-id to template create or environment create for a private repository. Its accessScope, for example Organization:acme, must cover the repository’s owner. --type filters by provider, for example GitHub or GitLab.
Cloud inventory
env0 inventory compares the cloud resources that exist with the ones env zero manages, through Cloud Compass. Cloud Compass reads the CloudQuery connection stored in the organization’s settings, so the CLI needs no CloudQuery credential. Every inventory command takes --organization, by name or id, and uses the stored organization when you omit it.
A resource is managed when Cloud Compass matched it to a resource in the state of an environment that still exists, or when it carries that environment’s env0_environment_id tag. An unmanaged resource is one no environment manages. It may belong to other tooling, such as CloudFormation, CDK, or Terraform run outside env zero, or it may have been created by hand.
Run inventory status first. Data is only as fresh as the last CloudQuery sync. When Cloud Compass cannot read its managed state, coverage returns managed, unmanaged, drifted, and managedPct as null for that group (? in tables), and list returns managed: null. Treat those values as unknown, not as zero.
inventory status
0 either way, so check .data.connected. When it is false, .data.reason says why, and every other inventory command fails with that reason. When it is true, .data.managedResources counts the managed resources. A count of 0 means no environment state has been matched yet, so every resource reads as unmanaged. An absent count means the count itself failed. Each integration’s lastRun.completedAt shows its last sync, and lastRun.statusReason: partial_success means some resources were not synced.
inventory coverage
coverage comes from a separate Cloud Compass count that includes nested records, so its totals can be higher than the page.totalCount of inventory list.
inventory list
coverage apply, and these flags are added:
page.totalCount is the full match count. The organization-wide list reads ten resources per request, so a large --limit or --all takes a while. Use coverage for counts. A good order is to run coverage, then coverage --account <name> --by type, then list --unmanaged --account <name> --type <label>.
inventory query
These tables are the most useful:
cloud_assetsholds every synced resource, withcloud,account,region,resource_type,name,tags,_cq_platform_id, and_cq_parent_id.resource_enrichmentrows withenrichment_source = 'envzero'link a resource, by_cq_platform_id, to the environment that manages it.insight_resourcesholds CloudQuery insights per resource, withseverityandtitle.
ORDER BY, the CLI runs it as SELECT * FROM (<query>) ORDER BY ALL so the pages line up. A query with its own ORDER BY is sent as written, so end it with a unique column such as _cq_platform_id. Otherwise rows that tie can repeat or go missing between pages. A request that runs past the server timeout exits 75 and is not retried, so narrow the query before you run it again.
Counting resource_enrichment rows undercounts what env zero manages, because it holds only state matches. Take managed and unmanaged counts from inventory status, coverage, or list.
Raw API requests
api
- Quote the path, because
?and&mean something to the shell. The path is relative to the API host and carries the query string. A full URL exits64, so your credentials only go to the configured host. - The stored organization and project are not added. Put
organizationIdorprojectIdwhere the endpoint’s reference page puts it: the path, the query, or the body. - The body is sent as is, as
application/jsonunless you pass your ownContent-Type. It is read only from--input, never from a bare pipe. - stdout gets the response body byte for byte, with no added newline.
-oshapes only the error envelope. - A non-2xx response is still printed to stdout, and the error envelope goes to stderr with
(HTTP <status>)at the end oferror.message. The exit code follows the status:2for other4xx,3for401and403,4for404,75for429and5xx, and1for anything unexpected, including a3xx, because redirects are not followed. - A path or method the API does not have answers
403with a message about an invalidAuthorizationheader, whatever your credentials. Iforganization listworks, check the method and path against the API reference. apinever retries and never asks for confirmation. A write that fails with a5xxor exit75may still have taken effect, so check before you send it again.
API keys
The CLI manages two kinds of API key:
Use an organization key for shared CI. Use a personal key on your own machine, or when actions must be attributed to you.
Both
create commands print apiKeySecret once. No command returns it again. The returned apiKeyId and apiKeySecret are the values for ENV0_API_KEY_ID and ENV0_API_KEY_SECRET. Keys do not expire.
api-key list
lastUsedAt. Custom roles show as their id.
api-key create
No command shows a key’s project roles after creation.
api-key revoke
id field from api-key list, which looks like auth0|abc123, not the apiKeyId field. Quote it, because | is a shell pipe. Revoking cannot be undone. The key leaves the list at once but can keep authenticating for a short time afterwards. --force is an alias for --yes.
personal-api-key list
personal-api-key create
personal-api-key revoke
id field, not apiKeyId. Another user’s key id exits 4.
Sign-in commands
login
See Sign-in and stored context.
status
0. Check .data.authenticated, not the exit code.
logout
Agent skill
skill install
AGENTS.md and the Copilot file are edited only between managed markers, so the rest of the file is kept. Running install again is safe. --global installs in your home directory, for claude and codex. --path writes to a file or directory you choose.
skill show
-o.
skill uninstall
Other commands
Telemetry
The CLI sends anonymous usage events and request traces: the command, output format, detected agent or CI system, authentication method, error type, exit code, CLI version, and organization id. Events use a random per-install id. They never include credentials, arguments, output, resource names or ids, or file paths. Telemetry never blocks or fails a command. To turn off both telemetry and tracing, setENV0_TELEMETRY_DISABLED=1 or DO_NOT_TRACK=1. env0 status shows whether each is on.