A Terraform CI job applied against the default workspace instead of `staging`. How does Terraform decide which CLI workspace is active, and how do you make that deterministic in a pipeline?
answer
- the selection is local, not in the repo
- .terraform/environment holds the name
- a fresh checkout starts in default
- TF_WORKSPACE states it in the job
- select after init, never before
basics
~20 sTerraform reads the active workspace from .terraform/environment in the working directory. A fresh CI checkout has no such file, so it silently uses default. Make it explicit with TF_WORKSPACE or a terraform workspace select step after init.
solid answer
~50 sThe selection made by `terraform workspace select` is stored locally in `.terraform/environment`, inside the same `.terraform` directory that `init` populates. That directory is not committed and is usually rebuilt from scratch on a runner, so a CI job that never selects anything runs in `default` — and if the config's default workspace happens to hold real infrastructure, the apply lands there. Fix it by making the workspace an explicit, failing-loud input: set the `TF_WORKSPACE` environment variable for the job, or run `terraform workspace select <name>` right after `init` and let the step fail if the workspace does not exist. Terraform 1.4 added `terraform workspace select -or-create <name>`, which creates it when missing — convenient for per-pull-request stacks, but for long-lived environments plain `select` is safer because a typo then fails instead of quietly building a new empty environment.
code
bash · 4 linesterraform init -input=false
terraform workspace select staging
terraform workspace show
terraform plan -input=false -out=tfplango deeper
Know that the workspace you selected is remembered on your machine, not in the repository, so a build server starts in the workspace called default unless it is told otherwise.
Name the file — .terraform/environment — and explain why it is absent on a fresh runner, plus the two ways to make selection explicit: the TF_WORKSPACE variable or a select step after init.
Show the failure mode you would guard against: the default workspace holding a real environment, so a silent fallback applies one environment's variables to another's state, and design the job so a missing workspace fails loudly.
Own the guarantee rather than the command: every pipeline states its target state explicitly, production is not reachable from a laptop's remembered selection, and the job log records which state was touched.
## Where the selection lives A CLI workspace selection is **local machine state**. When you run `terraform workspace select staging`, Terraform writes the name into `.terraform/environment` in the working directory. Nothing about that lands in your `.tf` files, your commit, or the backend's view of the world. That has two consequences. On a laptop, the selection persists silently across days — you can come back on Monday still selected into an environment you forgot about. On a runner, `.terraform` is created fresh by `init` (or restored from a cache), so unless the job selects a workspace, the run happens in `default`. ## Why the failure is quiet Nothing errors. `default` always exists, so `plan` and `apply` succeed against whatever state that workspace holds. Two variants of the bug show up in real pipelines: 1. The default workspace holds nothing, so the job proposes creating an entire environment from scratch. Loud, but only if someone reads the plan. 2. The default workspace *is* one of your real environments — very common, because the first environment ever built was the one nobody bothered to name. Then the staging pipeline applies staging's variables to that environment's state. ## Making it deterministic **Set `TF_WORKSPACE`.** Terraform honours this environment variable to select the workspace non-interactively, which suits CI because the value comes from the job definition, is visible in the job configuration, and cannot be forgotten by a step ordering mistake. **Or select explicitly after init**, and let failure be the default outcome: ```bash terraform init -input=false terraform workspace select staging # fails if it does not exist terraform plan -input=false -out=tfplan ``` Because the backend must be initialized before Terraform can enumerate workspaces, this step always comes after `init`, never before. **For ephemeral stacks**, Terraform 1.4 and later accept `terraform workspace select -or-create pr-1234`, which creates the workspace if it is missing. That is exactly right for a per-pull-request environment whose first run must bootstrap itself, and exactly wrong for long-lived environments — there a typo silently creates `stagng` with an empty state and builds a duplicate estate instead of failing. ## Related hygiene - Add a guard that the plan is non-empty-in-the-expected-way, or at minimum echo `terraform workspace show` into the job log so the audit trail records which state was touched. - Do not commit `.terraform`. Beyond the workspace file, it holds provider binaries and a cached backend configuration; a stale cached backend on a runner is its own class of confusion. - Remember the local counterpart of this bug: a developer applying from a laptop inherits whatever selection is on disk. Teams that keep workspaces at all usually also keep production out of them entirely, so the worst case of a wrong selection is a rebuilt sandbox. ## The one-line answer The active workspace is a local file, not a property of the repository — so any pipeline that does not state the workspace explicitly is running in `default` by accident.
- Why can `terraform workspace select` not run before `terraform init`?Workspaces are enumerated from the backend, and the backend is only configured and initialized by `init`. Before that, Terraform has no idea where state lives or which workspaces exist, so any workspace subcommand errors out asking you to run init first.
- When is `-or-create` the right choice, and when is it dangerous?Right for ephemeral stacks whose first pipeline run must bootstrap itself — a per-pull-request environment, a per-developer sandbox. Dangerous for long-lived environments: a typo in the name then creates a brand-new empty workspace and the job happily builds a duplicate estate instead of failing on the mistake.
- Should `.terraform` be cached or committed in CI?Never committed — it holds provider binaries, a cached backend configuration and the local workspace selection, none of which belong in a repository. Caching it between jobs is a performance choice, and if you do, the workspace must still be set explicitly, because a cached selection from another job is exactly the ambiguity you are trying to remove.
saying these in an interview costs you the question
- Thinks the selected workspace is stored in the state file
- Assumes a fresh checkout keeps the last workspace used
- Runs terraform workspace select before init
- Uses -or-create for production environments
- Believes a wrong workspace makes Terraform error out