Skip to main content
RunBook Academy

TerraformXV · Environment Architecture and State BoundariesProduction Terraform

Terraform Workspaces: When and When Not

Intermediate⏱ ~12 minbash

What you'll learn

  • Use Terraform workspaces for per-developer sandboxes on a shared backend
  • Recognise when workspaces actively hurt production isolation
  • Avoid the workspace-select mistake
  • Migrate from workspaces to per-directory layouts for production

Prerequisites

None — start here.

Verified against Terraform CLI 1.9.x · OpenTofu 1.7.x · HCL 2.0 · bpg/proxmox provider 0.66+ · hashicorp/local provider 2.5+ · hashicorp/null provider 3.2+ · hashicorp/random provider 3.6+ · hashicorp/http provider 3.4+ · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · 2026-08-13

Not yet marked complete on this device.

Terraform workspaces are the OSS feature for managing multiple named states within a single backend. The lesson teaches what workspaces do, what they do not do, when they are the right tool, and when they actively undermine production isolation.

What workspaces are

A Terraform workspace is a named state within a single backend configuration. Every Terraform configuration has at least one workspace (the default). A configuration can declare more:

terraform workspace new dev
terraform workspace new staging
terraform workspace new prod

Each workspace is a distinct state file in the same bucket. The backend configuration is shared; the state is per-workspace:

s3://mycompany-terraform-state/
└── env:/
    ├── prod/terraform.tfstate
    ├── staging/terraform.tfstate
    └── dev/terraform.tfstate

The workspace is selected with terraform workspace select:

terraform workspace select prod
terraform plan -var-file=prod.tfvars

The active workspace is also exposed at runtime as terraform.workspace. The variable is useful for branching on the workspace in the configuration:

locals {
  config = {
    dev = {
      cidr_block    = "10.10.0.0/16"
      instance_type = "t3.medium"
    }
    staging = {
      cidr_block    = "10.20.0.0/16"
      instance_type = "m5.large"
    }
    prod = {
      cidr_block    = "10.30.0.0/16"
      instance_type = "m5.xlarge"
    }
  }
  workspace_config = local.config[terraform.workspace]
}

What workspaces share

This is the critical list. Workspaces share:

  • The backend configuration (bucket, region, dynamodb_table).
  • The lock table (one DynamoDB table per backend, regardless of workspace count).
  • The IAM principal used by the Terraform execution context.
  • The provider configuration.
  • The module sources.

A workspace is not a boundary. A workspace is a key prefix on the state file. The boundary that a workspace provides is the state boundary, which the workspace shares with every other workspace in the same backend.

A team that runs prod and staging as workspaces in the same backend has:

  • One state bucket (with two state files).
  • One lock table.
  • One IAM principal.

A bad apply in staging can lock the lock table, blocking a legitimate apply in prod. A leaked credential that can read the state bucket can read both prod/terraform.tfstate and staging/terraform.tfstate. A misconfigured IAM role that grants prod permissions also grants staging permissions.

When workspaces help

Workspaces are the right tool for:

Per-developer sandboxes. A team of N engineers can each have a workspace on a shared dev backend. The state files are small, the apply is fast, and the sandboxes do not interact with each other. The cost of a sandbox mistake is small.

terraform workspace new alice-dev
terraform workspace new bob-dev
terraform workspace new charlie-dev

Short-lived feature branches. A feature branch can have its own workspace for the duration of the branch. The workspace is destroyed when the branch is merged or abandoned.

terraform workspace new feature-x
terraform workspace select feature-x

Experimentation within a single environment. A team can test a refactor in a workspace without touching the production state. The workspace is the scratchpad.

terraform workspace new refactor-test
terraform workspace select refactor-test
terraform plan -target=module.network

The common property: the workspaces are short-lived, the blast radius is small, and the cost of a workspace mistake is acceptable.

When workspaces hurt

Workspaces are the wrong tool for:

Production isolation. Production needs a separate backend, separate credentials, and separate lock table. Workspaces provide none of these.

Multi-account estates. Production and non-production in different AWS accounts cannot be workspaces in the same backend - the credentials are different.

Compliance regimes that require separate audit trails. A compliance audit that requires “production state is in a separate account from non-production state” cannot be satisfied by workspaces in a single backend.

Long-lived environments with distinct operational ownership. Two environments owned by different teams need separate backends and credentials; the workspace boundary is too thin.

The workspace select mistake

The most common workspace-related incident is the workspace select mistake. An operator intends to apply to staging and accidentally applies to prod:

# Operator believes they are in staging
terraform workspace show
# Output: staging

# But the terminal session was opened in another directory
# and `terraform workspace select prod` was run earlier

terraform apply -var-file=staging.tfvars
# The apply runs against the prod workspace
# with staging variable values
# Result: production resources scaled to staging sizing

The mistake is silent. The plan output looks correct (because the variables are correct). The apply runs against the wrong workspace. The damage is visible only after the apply completes.

The discipline:

  • The CI/CD pipeline selects the workspace explicitly. The pipeline runs terraform workspace select <env> before the apply. The pipeline variable is the source of truth.
  • The apply is gated on the workspace name. The pipeline asserts the workspace name matches the expected environment before the apply runs.
  • The -var-file is named to match the workspace. A pipeline that selects prod and passes staging.tfvars fails the assertion.
# CI/CD assertion
EXPECTED=prod
ACTUAL=$(terraform workspace show)
if [[ "$ACTUAL" != "$EXPECTED" ]]; then
  echo "ERROR: workspace mismatch (expected $EXPECTED, got $ACTUAL)"
  exit 1
fi

The migration to per-directory layouts

A team that has been using workspaces for production isolation should migrate to a per-directory layout. The migration is a one-time operation.

The steps:

  1. Snapshot the current state. Pull the state from each workspace and save it locally.
for ws in dev staging prod; do
  terraform workspace select $ws
  terraform state pull > $ws.tfstate
done
  1. Create the per-directory layout. Move each environment to its own directory with its own backend configuration.

  2. Push the state to the new backend. Use terraform init -migrate-state to migrate each state to its new backend.

cd envs/prod
terraform init -backend-config=prod.tfbackend -migrate-state
  1. Destroy the old workspaces. Once the new layout is verified, destroy the old workspaces.
terraform workspace select prod
terraform state rm $(terraform state list)  # in the old root
terraform workspace delete prod
  1. Update the CI/CD pipeline. The pipeline now selects the directory, not the workspace.

When to keep workspaces

A team does not need to migrate away from workspaces entirely. The right pattern is:

  • Workspaces for sandboxes and short-lived feature branches.
  • Directories for production and non-production environments.

The combination is deliberate. The workspaces layer on top of the dev directory. The production directory has no workspaces because production isolation is enforced by the directory and backend boundaries.

infra/
├── envs/
│   ├── dev/
│   │   ├── main.tf
│   │   ├── backend.tf        # S3 backend, shared with sandboxes
│   │   └── dev.tfvars
│   └── prod/
│       ├── main.tf
│       ├── backend.tf        # S3 backend, separate bucket/role
│       └── prod.tfvars       # NO workspaces in prod

The dev directory may have multiple workspaces (alice-dev, bob-dev, feature-x). The prod directory has exactly one workspace: default.

Validation

The validation commands for workspace discipline:

# List workspaces in the current backend
terraform workspace list
* default
  alice-dev
  bob-dev
  feature-x

The active workspace is marked with *. The list should match the intent.

# Confirm the workspace before any apply
terraform workspace show
# List the resources in the current workspace
terraform state list

The state list should match the intent of the workspace (e.g. the prod workspace should contain production resources).

What comes next

The next lesson (in the following module) is state operations in production: the state mv, state rm, and state import commands that are the surgical tools for state recovery.

Verification

  • terraform workspace list from each environment directory shows the expected workspaces (or default if workspaces are not in use).
  • terraform workspace show returns the active workspace before every apply; the CI/CD pipeline asserts the workspace name matches the expected environment.
  • terraform state list from the prod workspace shows only production resources; no staging or dev resources.
  • The production backend configuration is in a separate directory from the dev backend configuration; the two are not workspaces in the same backend.
  • The IAM role used for production applies cannot read the state bucket used for non-production workspaces.

Knowledge check · 6 questions

  1. Q1. What is the correct use of Terraform workspaces?

  2. Q2. What does a workspace select mistake look like in production?

  3. Q3. Why should production and non-production NOT be workspaces in the same backend?

  4. Q4. A team using workspaces for production isolation should migrate to a per-directory layout.

  5. Q5. Which of the following are correct uses of Terraform workspaces? (Select all that apply.)

  6. Q6. A team has been using workspaces for production and staging in the same S3 backend. An auditor requires production state to be in a separate account. What is the migration plan?

Passing score: 75%. Answers are checked in this browser.