| Tested on | Ubuntu 26.04 LTS (Resolute Raccoon) |
|---|---|
| Package | terraform 1.15.8-1 |
| Applies to | Any host with Terraform installed |
| Lab environment | Single Ubuntu VM with Terraform — Terraform lab environment on Ubuntu |
| Privilege | Normal user |
| Scope | Terraform CLI workspaces — default workspace, workspace list, new, select, show, delete, terraform.workspace, same configuration with separate state, local state file layout, when to use workspaces, CLI vs HCP Terraform workspace comparison, and common mistakes. Does not cover HCP workspace administration, remote execution, backend configuration depth, or variable sets. |
| Related guides | Terraform state explained Terraform backends and remote state terraform state command HCP Terraform workspaces and projects Terraform Associate certification course |
Terraform CLI workspaces let one root module keep multiple state snapshots. You run the same *.tf files, but terraform apply in the dev workspace does not overwrite resources tracked in test or default.
Start in any initialized directory and list workspaces:
terraform workspace listSample output:
* defaultThe asterisk marks the active workspace. A fresh directory only has default.
Same configuration
│
├── default → state A
├── dev → state B
└── test → state CEach branch is a separate state instance tied to the same backend and configuration — not a separate Terraform project.
What is a Terraform CLI workspace?
A CLI workspace is a named state partition for one working directory. Terraform stores resource addresses, provider metadata, and outputs in the state file for the workspace you have selected.
Workspaces do not copy or fork your configuration tree. They do not create a second root module on disk. If you rename a resource in main.tf, that change applies to whichever workspace is active when you run plan or apply.
Every initialized Terraform working directory starts with the default workspace. You never run terraform workspace new default; Terraform creates it implicitly. Named workspaces such as dev or test are optional extras you add when you need parallel state.
Backends that support multiple CLI workspaces store each workspace as a separate state instance within that backend configuration. See Terraform backends and remote state for how backends store those snapshots. This lesson focuses on the CLI commands and local file layout.
Lab setup
Create an isolated directory for this lesson:
mkdir -p ~/terraform-labs/terraform-workspacesMove into it — later commands assume this path:
cd ~/terraform-labs/terraform-workspacesAdd main.tf with a terraform_data resource that reads the active workspace name:
resource "terraform_data" "example" {
input = "application-${terraform.workspace}"
}
locals {
environment = terraform.workspace
}
output "workspace_name" {
value = terraform.workspace
}
output "environment" {
value = local.environment
}Initialize the directory:
terraform initSample output:
Initializing the backend...
Initializing provider plugins...
- terraform.io/builtin/terraform is built in to Terraform
Terraform has been successfully initialized!terraform_data is built in, so no provider download is required for this lab.
Create and switch Terraform workspaces
Confirm you are still on default before creating named workspaces:
terraform workspace showSample output:
defaultCreate a dev workspace. Terraform creates the workspace and switches to it in one step:
terraform workspace new devSample output:
Created and switched to workspace "dev"!
You're now on a new, empty workspace. Workspaces isolate their state,
so if you run "terraform plan" Terraform will not see any existing state
for this configuration.List workspaces again — the asterisk moved to dev:
terraform workspace listSample output:
default
* devterraform workspace show prints only the active name without the list formatting:
terraform workspace showSample output:
devSwitch back to an existing workspace when you need to work in another state partition:
terraform workspace select defaultSample output:
Switched to workspace "default".Create a third workspace for the apply walkthrough later:
terraform workspace new testSample output:
Created and switched to workspace "test"!
You're now on a new, empty workspace. Workspaces isolate their state,
so if you run "terraform plan" Terraform will not see any existing state
for this configuration.After this command you are on test, not default or dev.
Deploy the same configuration in multiple workspaces
Apply in default first. Switch there if you are on another workspace:
terraform workspace select defaultRun apply so Terraform creates the resource in the default state file:
terraform apply -auto-approveSample output:
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ environment = "default"
+ workspace_name = "default"
terraform_data.example: Creating...
terraform_data.example: Creation complete after 0s [id=d0c19ab4-03f8-29c6-cc2b-dd4b76e3e7ec]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
environment = "default"
workspace_name = "default"The input value application-default came from terraform.workspace inside the same main.tf you will reuse elsewhere.
Switch to dev and apply the identical configuration there:
terraform workspace select devdev started empty, so the next apply creates a separate object:
terraform apply -auto-approveSample output:
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ environment = "dev"
+ workspace_name = "dev"
terraform_data.example: Creating...
terraform_data.example: Creation complete after 0s [id=267df21a-81e1-cc8d-7367-d445e8ffe3ef]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
environment = "dev"
workspace_name = "dev"The resource address terraform_data.example is the same in both workspaces, but the state IDs differ — proof that state is isolated.
Repeat for test:
terraform workspace select testApply once more in the third workspace:
terraform apply -auto-approveSample output:
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ environment = "test"
+ workspace_name = "test"
terraform_data.example: Creating...
terraform_data.example: Creation complete after 0s [id=3e5c2a3b-20b6-d6e6-1ff7-7e08f6629643]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
environment = "test"
workspace_name = "test"Switch to dev and read its output — you should see dev, not test or default:
terraform workspace select devRead the output Terraform recorded for this workspace:
terraform output workspace_nameSample output:
"dev"Select test and confirm that workspace still owns its own value:
terraform workspace select testThe test workspace should report its own name, not dev:
terraform output workspace_nameSample output:
"test"Each workspace keeps its own terraform_data.example entry. Use terraform state command tools such as terraform state list when you need to inspect addresses inside a specific workspace.
Use terraform.workspace in configuration
The terraform.workspace expression returns the name of the active workspace as a string. In this lab it feeds both the resource input and a locals block:
resource "terraform_data" "example" {
input = "application-${terraform.workspace}"
}
locals {
environment = terraform.workspace
}That pattern works for suffixes, tags, or lightweight naming differences. Keep the branching shallow. When main.tf grows large count or for_each expressions keyed only on terraform.workspace, every environment shares one file and one review surface — mistakes in a dev conditional can leak into prod during the same merge.
Prefer workspace names for small, repeatable instances of the same stack — not as a substitute for separate variable files, backends, or root modules when environments truly diverge.
Where workspace state files live
With the default local backend, Terraform stores state on disk. The default workspace uses the root file; every other workspace gets its own subdirectory.
List the state paths after applying in all three workspaces:
ls -la terraform.tfstate terraform.tfstate.d/Sample output:
-rw-r--r-- 1 root root 1025 Aug 12 10:26 terraform.tfstate
terraform.tfstate.d/:
total 16
drwxr-xr-x 4 root root 4096 Aug 12 10:27 .
drwxr-xr-x 4 root root 4096 Aug 12 10:27 ..
drwxr-xr-x 2 root root 4096 Aug 12 10:27 dev
drwxr-xr-x 2 root root 4096 Aug 12 10:27 testNamed workspace state files sit under terraform.tfstate.d/<workspace>/terraform.tfstate. The layout is:
default→terraform.tfstatein the working directory rootdev,test, and other named workspaces →terraform.tfstate.d/<name>/terraform.tfstate
Remote backends hide this directory layout, but when the backend supports CLI workspaces, the selected workspace determines which remote state instance Terraform reads and writes. Workspace semantics are a state concern, not a second copy of your module source.
Delete a Terraform workspace
Removing a workspace deletes its state metadata, not necessarily the real infrastructure. Terraform blocks unsafe deletes.
Try deleting dev while resources still exist and you are on default:
terraform workspace select defaultFrom default, try deleting dev while it still tracks a resource:
terraform workspace delete devSample output:
╷
│ Error: Workspace is not empty
│
│ Workspace "dev" is currently tracking the following resource instances:
│ - terraform_data.example
│
│ Deleting this workspace would cause Terraform to lose track of any
│ associated remote objects, which would then require you to delete them
│ manually outside of Terraform. You should destroy these objects with
│ Terraform before deleting the workspace.
│
│ If you want to delete this workspace anyway, and have Terraform forget
│ about these managed objects, use the -force option to disable this safety
│ check.
╵Terraform also refuses to delete the workspace you are using. Select dev and try again:
terraform workspace select devDeletion should also fail while dev is the active workspace:
terraform workspace delete devSample output:
Workspace "dev" is your active workspace.
You cannot delete the currently active workspace. Please switch
to another workspace and try again.The safe workflow is destroy, switch away, then delete. Destroy resources in test first:
terraform workspace select testDestroy the managed object in test so the workspace state becomes empty:
terraform destroy -auto-approveSample output:
terraform_data.example: Destroying... [id=3e5c2a3b-20b6-d6e6-1ff7-7e08f6629643]
terraform_data.example: Destruction complete after 0s
Destroy complete! Resources: 1 destroyed.Repeat in dev:
terraform workspace select devClear dev the same way:
terraform destroy -auto-approveSample output:
terraform_data.example: Destroying... [id=267df21a-81e1-cc8d-7367-d445e8ffe3ef]
terraform_data.example: Destruction complete after 0s
Destroy complete! Resources: 1 destroyed.Clean up default as well before you delete the extra workspaces:
terraform workspace select defaultFinish cleanup in default before you remove the named workspaces:
terraform destroy -auto-approveSample output:
terraform_data.example: Destroying... [id=d0c19ab4-03f8-29c6-cc2b-dd4b76e3e7ec]
terraform_data.example: Destruction complete after 0s
Destroy complete! Resources: 1 destroyed.With empty state, delete test from any non-active workspace:
terraform workspace delete testSample output:
Deleted workspace "test"!Delete dev the same way:
terraform workspace delete devSample output:
Deleted workspace "dev"!Only default remains:
terraform workspace listSample output:
* defaultThe terraform.tfstate.d/ subdirectories for removed workspaces are gone. terraform workspace delete -force can drop state without destroy, which leaves real objects orphaned — reserve that flag for deliberate state abandonment.
When should you use Terraform workspaces?
CLI workspaces fit when you need multiple state instances of essentially the same configuration on the same backend access path.
Good fits include:
- Ephemeral or personal sandboxes that share one module layout
- Short-lived test copies before promoting changes through a separate promotion pipeline
- Multiple instances of the same pattern where only state — not credentials or module source — should differ
Use separate root modules, directories, or repositories when environments need:
- Different cloud credentials or blast-radius boundaries
- Materially different resource sets, not just a name suffix
- Distinct ownership, approval, or retirement schedules
- Hard isolation between production and non-production backends
Workspaces are a state switch, not an access-control boundary. Anyone who can run terraform workspace select prod and apply shares the same code and backend credentials as every other workspace in that directory.
Terraform CLI workspaces vs HCP Terraform workspaces
HashiCorp uses the word workspace in two places. They are not interchangeable.
Terraform CLI workspace
→ separate state instances associated with one configuration/backend
HCP Terraform workspace
→ managed unit containing configuration/state/variables/run context| CLI workspace | HCP Terraform workspace | |
|---|---|---|
| What it partitions | State inside one working directory | Runs, variables, remote state, and VCS linkage in HCP |
| Configuration | Same *.tf on disk |
Often tied to a VCS repo and HCP variable sets |
| Created by | terraform workspace new |
HCP Terraform UI or API/automation |
| Typical scope | Local or shared backend object selection | Organization-wide collaboration and governance |
This lesson covers CLI commands only. For projects, run triggers, and managed separation in HashiCorp Cloud Platform, see HCP Terraform workspaces and projects.
Common workspace mistakes
| Mistake | Why it hurts | Safer habit |
|---|---|---|
| Forgetting which workspace is active | You apply dev changes to prod state | Run terraform workspace show before plan/apply; print workspace in CI logs |
| Assuming workspace changes code automatically | Only state selection changes; main.tf is shared |
Treat workspace as a state pointer, not an environment file |
| Using workspace as a security boundary | Same credentials and backend config apply to all workspaces | Split directories or backends when isolation matters |
| Confusing CLI and HCP workspaces | Documentation and runbooks talk past each other | Name the product: CLI workspace vs HCP workspace |
| Deleting a workspace that still tracks resources | Orphaned real infrastructure | terraform destroy in that workspace first, then delete |
Heavy terraform.workspace branching |
One merge can affect every environment | Prefer variables, separate roots, or modules per environment tier |
Keep these commands handy:
terraform workspace listshow prints only the active workspace name:
terraform workspace showCreate a workspace and switch to it immediately:
terraform workspace new NAMESwitch to an existing workspace without creating it:
terraform workspace select NAMERemove an empty workspace after destroy:
terraform workspace delete NAMElist shows every workspace and marks the active one. show prints only the current name — useful in scripts. new creates and selects; select switches; delete removes an empty workspace after you destroy its resources.
References
Summary
Terraform CLI workspaces give one root module several state snapshots. You list them with terraform workspace list, create names with new, switch with select, and read the active name with show or the terraform.workspace expression in configuration. The lab proved the point: the same terraform_data.example address existed in default, dev, and test, but each workspace held a different resource ID and output value.
On the default local backend, default state lives in terraform.tfstate while named workspaces use terraform.tfstate.d/<name>/terraform.tfstate. Remote backends hide those paths, but when the backend supports CLI workspaces, the selected workspace determines which remote state instance Terraform loads. Deletion is deliberate: destroy resources first, switch away from the workspace, then run terraform workspace delete — Terraform blocks deletes on the active workspace and on workspaces that still track objects.
Workspaces suit parallel instances of the same stack when backend access and configuration shape align. They are weaker when environments need different credentials, lifecycle ownership, or substantially different infrastructure — separate roots or HCP-managed workspaces may fit better. The name collision with HCP Terraform workspaces trips up many teams; CLI workspaces partition state inside one directory, while HCP workspaces bundle collaboration, variables, and remote operations in the cloud product.
For remote state storage patterns, continue with Terraform backends and remote state. When you manage HCP-side workspace design, open HCP Terraform workspaces and projects.

