Workspaces and Environment Strategies¶
Overview¶
Running dev and production from the same directory without separation is how terraform destroy accidents happen. Terraform workspaces multiply state instances for the same configuration — dev, staging, and prod each get isolated state while code stays identical.
This tutorial covers terraform workspace commands, terraform.workspace, environment separation strategies (workspaces vs directories vs branches), and production cautions. The lab under ~/rebash-terraform/module-12 creates dev and staging workspaces with isolated Docker containers and separate state files.
This is Tutorial 14 in Module 12: Workspaces of the REBASH Academy Terraform for Cloud & DevOps Engineers series.
Prerequisites¶
- Data Sources and Existing Infrastructure
- Remote State and Backends
- Terraform CLI ≥ 1.5
Learning Objectives¶
By the end of this tutorial, you will be able to:
- Create, list, select, and delete Terraform workspaces
- Use
terraform.workspacein expressions for environment-specific behaviour - Explain isolated state paths under
terraform.tfstate.d/ - Compare workspace, directory, and branch environment models
- Describe when workspaces are insufficient for production isolation
Architecture¶
One configuration code path; workspace selection switches which state file Terraform loads under terraform.tfstate.d/.
Theory¶
What it is¶
Workspaces are named state instances for a single root module:
terraform workspace new dev
terraform workspace new staging
terraform workspace select dev
terraform apply
In HCL:
locals {
env_config = {
dev = { replicas = 1 }
staging = { replicas = 2 }
prod = { replicas = 3 }
}
replicas = local.env_config[terraform.workspace].replicas
}
Default workspace is default — many teams create explicit dev instead of using default for production-adjacent work.
State storage: local backend places files in terraform.tfstate.d/<workspace>/terraform.tfstate. Remote backends use workspace key prefixes (S3) or separate workspace objects (Terraform Cloud).
Why it matters¶
Workspaces let engineers reuse one module tree with different state — fast context switching for smaller teams. Combined with remote backends, each workspace can map to different state keys. Understand limits: workspaces share the same backend credentials and provider config unless you add logic — they are not hard multi-account isolation alone.
How it works¶
terraform workspace list— shows current selection (*).terraform workspace select NAME— switches active state.- Plan/apply operates only on current workspace state.
terraform.workspaceinterpolates active workspace name in expressions.terraform workspace delete NAME— removes workspace (must be empty of managed resources or force after destroy).
| Strategy | Isolation | Same code? |
|---|---|---|
| Workspaces | State only (shared backend config) | Yes |
Directory per env (env/dev, env/prod) | State + different var files | Often duplicated root |
| Branch per env | Process isolation | Yes in Git |
| Separate stacks + remote state | Strong ownership boundaries | Partial reuse via modules |
Key concepts and comparisons¶
| Model | Good for | Weak for |
|---|---|---|
| Workspaces | Quick env toggles; small teams | Hard multi-account blast walls |
| Directory layout | Different backends per env | Duplication without modules |
| Terraform Cloud workspaces | RBAC, run tasks, policy | Cost; SaaS dependency |
-var-file only (single workspace) | Simple two-tier | Easy to apply wrong tfvars |
Common pitfalls¶
- Applying in wrong workspace — prod destroy from dev laptop.
- Using
defaultfor production — unclear intent; rename explicitly. - Workspace-only prod isolation — same AWS creds manage all workspaces.
- Deleting workspace with resources — fails until destroy in that workspace.
- Assuming workspaces replace tfvars — you still need variable values per env.
Hands-on Lab¶
Objective¶
Create dev and staging workspaces, apply environment-specific Docker containers with different replica labels, prove separate state files and distinct container IDs, and validate with an evidence script under ~/rebash-terraform/module-12.
Prerequisites¶
- Terraform CLI ≥ 1.5
- Docker Engine running (
docker infosucceeds) - Completed Module 8–11 labs
Lab environment¶
Local backend — workspace state files appear under terraform.tfstate.d/.
Real-world scenario¶
A platform team uses one service module for dev and staging, deploying different container replica counts per workspace. Before promoting workspace patterns to S3 backends, you prove state isolation locally so a staging destroy never removes dev containers.
Step-by-step tasks¶
Task 1 – Configuration with terraform.workspace and Docker¶
Create versions.tf:
terraform {
required_version = ">= 1.5.0"
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "~> 3.0"
}
}
}
Create providers.tf:
Create variables.tf:
Create locals.tf:
locals {
workspace_replicas = {
dev = 1
staging = 2
default = 1
}
replicas = lookup(local.workspace_replicas, terraform.workspace, 1)
}
Create main.tf:
resource "docker_image" "env_marker" {
name = "nginx:1.27-alpine"
keep_locally = true
}
resource "docker_container" "env_marker" {
count = local.replicas
name = "rebash-${terraform.workspace}-${count.index}"
image = docker_image.env_marker.image_id
labels = {
workspace = terraform.workspace
replica = tostring(count.index)
owner = var.owner
managed_by = "terraform"
}
}
Create outputs.tf:
output "active_workspace" {
value = terraform.workspace
}
output "replica_count" {
value = local.replicas
}
output "container_ids" {
value = docker_container.env_marker[*].id
}
Run:
cd ~/rebash-terraform/module-12
terraform init
terraform validate
echo "config OK" | tee config-ok.txt
Expected output
Validate succeeds in default workspace.
Task 2 – Create dev workspace and apply¶
Run:
cd ~/rebash-terraform/module-12
terraform workspace new dev
terraform workspace select dev
terraform apply -auto-approve
terraform output -raw active_workspace | tee ws-dev.txt
terraform output -raw replica_count | tee replicas-dev.txt
test "$(cat ws-dev.txt)" = "dev"
test "$(cat replicas-dev.txt)" = "1"
docker ps --filter "label=workspace=dev" --format '{{.Names}}' | tee dev-containers.txt
grep -q 'rebash-dev-0' dev-containers.txt
test -f ~/rebash-terraform/module-12/terraform.tfstate.d/dev/terraform.tfstate
echo "dev workspace OK" | tee dev-ws-ok.txt
Expected output
Active workspace dev; one container rebash-dev-0; state file under terraform.tfstate.d/dev/.
Task 3 – Create staging workspace and apply¶
Run:
cd ~/rebash-terraform/module-12
terraform workspace new staging
terraform workspace select staging
terraform apply -auto-approve
terraform output -raw replica_count | tee replicas-staging.txt
test "$(cat replicas-staging.txt)" = "2"
docker ps --filter "label=workspace=staging" --format '{{.Names}}' | tee staging-containers.txt
grep -q 'rebash-staging-0' staging-containers.txt
grep -q 'rebash-staging-1' staging-containers.txt
terraform workspace select dev
docker ps --filter "label=workspace=dev" --format '{{.Names}}' | tee dev-check.txt
grep -q 'rebash-dev-0' dev-check.txt
test -f ~/rebash-terraform/module-12/terraform.tfstate.d/staging/terraform.tfstate
echo "staging workspace OK" | tee staging-ws-ok.txt
Expected output
Staging has two containers; dev container still running — states are isolated.
Task 4 – Workspace evidence script¶
Create workspace-evidence.sh:
#!/usr/bin/env bash
set -euo pipefail
cd ~/rebash-terraform/module-12
terraform workspace select dev
DEV_COUNT="$(docker ps --filter 'label=workspace=dev' --format '{{.ID}}' | wc -l | tr -d ' ')"
terraform workspace select staging
STAGING_COUNT="$(docker ps --filter 'label=workspace=staging' --format '{{.ID}}' | wc -l | tr -d ' ')"
test "$DEV_COUNT" = "1"
test "$STAGING_COUNT" = "2"
terraform workspace list | tee workspace-list.txt
grep -q 'dev' workspace-list.txt
grep -q 'staging' workspace-list.txt
echo "workspace-evidence PASS" | tee workspace-evidence-pass.txt
Run:
chmod +x ~/rebash-terraform/module-12/workspace-evidence.sh
~/rebash-terraform/module-12/workspace-evidence.sh
Expected output
Dev has 1 container, staging has 2; evidence script passes.
Validation steps¶
- Created dev and staging workspaces
-
terraform.workspacedrove replica count viacount - Separate state files under
terraform.tfstate.d/ - Switching workspace changes running containers without code edits
- Evidence script confirms distinct container sets per workspace
Common errors and fixes¶
| Error | Cause | Fix |
|---|---|---|
| Workspace already exists | Re-run new | Use select instead of new |
| Cannot delete workspace | Resources remain | select workspace; destroy; then delete |
| Same output both workspaces | Wrong workspace selected | terraform workspace show |
| lookup default missing key | Workspace not in map | Extend workspace_replicas map |
| Container name conflict | Leftover container from manual run | docker rm -f orphan; re-apply |
Challenge exercise¶
Add prod to workspace_replicas with value 3, create workspace, apply, and verify without touching dev/staging containers:
cd ~/rebash-terraform/module-12
# Add prod = 3 to locals.workspace_replicas in locals.tf
terraform workspace new prod
terraform workspace select prod
terraform apply -auto-approve
docker ps --filter "label=workspace=prod" --format '{{.Names}}' | wc -l | grep -q '^3$'
terraform workspace select dev
docker ps --filter "label=workspace=dev" --format '{{.Names}}' | wc -l | grep -q '^1$'
echo "prod workspace challenge OK"
Expected output
Prod shows 3 containers; dev unchanged at 1.
Learning outcomes¶
- Workspace CLI workflow with real infrastructure
- terraform.workspace driving resource count
- Physical state isolation paths
- Safe workspace switching habits with operational proof
Cleanup¶
cd ~/rebash-terraform/module-12
terraform workspace select prod 2>/dev/null && terraform destroy -auto-approve || true
terraform workspace select staging && terraform destroy -auto-approve
terraform workspace select dev && terraform destroy -auto-approve
terraform workspace select default
terraform workspace delete prod 2>/dev/null || true
terraform workspace delete staging
terraform workspace delete dev
rm -f config-ok.txt ws-dev.txt replicas-dev.txt dev-containers.txt dev-ws-ok.txt \
replicas-staging.txt staging-containers.txt dev-check.txt staging-ws-ok.txt \
workspace-list.txt workspace-evidence-pass.txt
rm -rf terraform.tfstate.d .terraform .terraform.lock.hcl terraform.tfstate terraform.tfstate.backup
Validation¶
- Completed module-12 workspace lab
- Can list workspace vs directory strategies
- Know where local workspace state files live
- Can explain workspace isolation limits
Code Walkthrough¶
- Explicit workspace names — avoid unnamed
defaultfor real envs. - lookup with default — handle unknown workspace keys safely.
- Select before plan — shell prompt or CI echo current workspace.
- Separate state files — verify path in
terraform.tfstate.d/. - Pair with var-files — workspace name + tfvars double confirmation in CI.
Security Considerations¶
- Workspaces share provider credentials unless using assume-role per workspace logic.
- Restrict
terraform workspace select prodvia CI-only production applies. - Remote backend IAM should scope workspace key prefixes per environment.
- Audit workspace deletes — state removal does not destroy resources if skipped.
- Do not rely on workspaces alone for regulatory environment separation.
Common Mistakes¶
Wrong workspace during apply
Classic source of prod incidents.
Fix: CI prints terraform workspace show; require approval for prod workspace.
Workspaces as multi-account strategy alone
Same AWS profile across workspaces — insufficient isolation.
Fix: Separate accounts + roles; directories or stacks per account.
Deleting workspace before destroy
Orphan resources keep running.
Fix: Destroy in workspace first; then workspace delete.
Best Practices¶
- Name workspaces after environments (
dev,staging,prod). - Combine workspaces with per-env tfvars and remote state key prefixes.
- Document workspace naming in README; ban ad-hoc workspace names.
- CI pipeline parameter selects workspace — not interactive shells for prod.
- For large orgs, prefer Terraform Cloud workspaces with RBAC over CLI-only workspaces.
Troubleshooting¶
| Symptom | Likely cause | Fix |
|---|---|---|
| Plan empty wrong env | Selected wrong workspace | terraform workspace show |
| Resource already exists | Duplicate apply another ws | Import or destroy other workspace copy |
| workspace delete fails | State has resources | Destroy first |
| Same state two workspaces | Backend misconfigured prefix | Fix remote workspace_key_prefix |
| terraform.workspace empty | Very old Terraform | Upgrade; use terraform 0.12+ |
Summary¶
Workspaces isolate state for the same configuration — enabling dev/staging/prod toggles with terraform workspace select. You applied distinct replica settings per workspace and verified separate state files. Next, Terraform Cloud and HCP Terraform adds remote runs, RBAC, and policy.
Interview Questions¶
1. What problem do Terraform workspaces solve?
Reveal answer
They let one root module code base maintain multiple isolated state files (environments) without duplicating directories. Switching workspace switches state — useful for dev/staging/prod iterations from the same checkout.
2. How do you reference the current workspace in HCL?
Reveal answer
Use the terraform.workspace built-in expression in locals, variables, or resource arguments — for example different CIDR maps or feature flags keyed by workspace name.
3. Where is workspace state stored locally?
Reveal answer
Under terraform.tfstate.d/<workspace_name>/terraform.tfstate when using the local backend. The default workspace uses terraform.tfstate in the root module directory (unless configured otherwise).
4. Compare workspaces vs separate directories per environment.
Reveal answer
Workspaces — same code, isolated state, fast switch; weaker isolation of backend config/credentials. Directories (env/dev, env/prod) — can point to different backends, var files, and provider aliases explicitly; more duplication unless modules extract common code.
5. Can workspaces replace separate AWS accounts for production isolation?
Reveal answer
No — workspaces only separate state by default; provider credentials are usually shared unless you wire assume-role maps per terraform.workspace. Production typically needs account-level isolation plus remote state RBAC.
6. What happens if you terraform destroy in the wrong workspace?
Reveal answer
You destroy resources tracked in that workspace's state — if prod workspace is selected, prod resources go. Prevention: CI gates, explicit workspace echo, separate AWS roles so dev credentials cannot destroy prod even if workspace wrong.
7. How do remote S3 backends map workspaces?
Reveal answer
Backend workspace_key_prefix (or default behaviour) stores state at different S3 keys per workspace — for example env:/dev/network/terraform.tfstate. Same bucket, different objects; locking still required.
8. When should you delete a workspace?
Reveal answer
After terraform destroy emptied that workspace's resources and the environment is decommissioned. Deleting a workspace removes its state metadata — do not delete if resources still exist unless you intentionally orphan them (rare, documented).
Related Tutorials¶
- Terraform course index
- Previous: Data Sources and Existing Infrastructure
- Next: Terraform Cloud and HCP Terraform
- Production Terraform Patterns
- Remote State and Backends