Terraform Workspaces with Examples

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:

bash
terraform workspace list

Sample output:

output
* default

The asterisk marks the active workspace. A fresh directory only has default.

text
Same configuration
       ├── default → state A
       ├── dev     → state B
       └── test    → state C

Each 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:

bash
mkdir -p ~/terraform-labs/terraform-workspaces

Move into it — later commands assume this path:

bash
cd ~/terraform-labs/terraform-workspaces

Add main.tf with a terraform_data resource that reads the active workspace name:

hcl
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:

bash
terraform init

Sample output:

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:

bash
terraform workspace show

Sample output:

output
default

Create a dev workspace. Terraform creates the workspace and switches to it in one step:

bash
terraform workspace new dev

Sample output:

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:

bash
terraform workspace list

Sample output:

output
default
* dev

terraform workspace show prints only the active name without the list formatting:

bash
terraform workspace show

Sample output:

output
dev

Switch back to an existing workspace when you need to work in another state partition:

bash
terraform workspace select default

Sample output:

output
Switched to workspace "default".

Create a third workspace for the apply walkthrough later:

bash
terraform workspace new test

Sample output:

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:

bash
terraform workspace select default

Run apply so Terraform creates the resource in the default state file:

bash
terraform apply -auto-approve

Sample output:

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:

bash
terraform workspace select dev

dev started empty, so the next apply creates a separate object:

bash
terraform apply -auto-approve

Sample output:

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:

bash
terraform workspace select test

Apply once more in the third workspace:

bash
terraform apply -auto-approve

Sample output:

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:

bash
terraform workspace select dev

Read the output Terraform recorded for this workspace:

bash
terraform output workspace_name

Sample output:

output
"dev"

Select test and confirm that workspace still owns its own value:

bash
terraform workspace select test

The test workspace should report its own name, not dev:

bash
terraform output workspace_name

Sample output:

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:

hcl
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:

bash
ls -la terraform.tfstate terraform.tfstate.d/

Sample output:

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 test

Named workspace state files sit under terraform.tfstate.d/<workspace>/terraform.tfstate. The layout is:

  • defaultterraform.tfstate in the working directory root
  • dev, 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:

bash
terraform workspace select default

From default, try deleting dev while it still tracks a resource:

bash
terraform workspace delete dev

Sample output:

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:

bash
terraform workspace select dev

Deletion should also fail while dev is the active workspace:

bash
terraform workspace delete dev

Sample output:

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:

bash
terraform workspace select test

Destroy the managed object in test so the workspace state becomes empty:

bash
terraform destroy -auto-approve

Sample output:

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:

bash
terraform workspace select dev

Clear dev the same way:

bash
terraform destroy -auto-approve

Sample output:

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:

bash
terraform workspace select default

Finish cleanup in default before you remove the named workspaces:

bash
terraform destroy -auto-approve

Sample output:

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:

bash
terraform workspace delete test

Sample output:

output
Deleted workspace "test"!

Delete dev the same way:

bash
terraform workspace delete dev

Sample output:

output
Deleted workspace "dev"!

Only default remains:

bash
terraform workspace list

Sample output:

output
* default

The 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.

text
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:

bash
terraform workspace list

show prints only the active workspace name:

bash
terraform workspace show

Create a workspace and switch to it immediately:

bash
terraform workspace new NAME

Switch to an existing workspace without creating it:

bash
terraform workspace select NAME

Remove an empty workspace after destroy:

bash
terraform workspace delete NAME

list 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.


Frequently Asked Questions

1. What is the default Terraform workspace?

Every initialized Terraform working directory starts with the default workspace named default. You do not create it manually. The asterisk in terraform workspace list marks the active workspace, which is default until you create or select another name.

2. Does switching workspaces change the Terraform configuration files?

No. Workspaces only change which state snapshot Terraform reads and writes. The same main.tf applies in every workspace unless you branch on terraform.workspace inside the configuration.

3. Can I delete the workspace I am currently using?

No. Terraform refuses to delete the active workspace. Switch to another workspace with terraform workspace select, then run terraform workspace delete on the empty workspace you want to remove.

4. Are Terraform CLI workspaces the same as HCP Terraform workspaces?

No. CLI workspaces are separate state instances for one configuration on your backend. HCP Terraform workspaces are managed units in HashiCorp Cloud Platform that bundle configuration, state, variables, and run context. The names overlap but the concepts differ.

5. Should I use workspaces for dev and prod environments?

Sometimes, when the same configuration and backend access model fit every environment and you only need isolated state. Use separate root modules or directories when credentials, lifecycle ownership, or infrastructure shape diverge materially.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)