Workspaces in Terraform
Workspaces let you maintain multiple distinct state files within a single backend configuration. The classic use case is managing dev, staging, and production environments from a single set of .tf files. Each workspace has its own state, so changes in one don’t affect the others.
Workspace Basics
# List workspaces (default workspace always exists)terraform workspace list# * default
# Create a new workspaceterraform workspace new production
# Switch to an existing workspaceterraform workspace select staging
# Show the current workspaceterraform workspace show
# Delete a workspace (must not be current, must be empty)terraform workspace delete devHow Workspaces Affect State Storage
With the S3 backend, workspaces create separate state file paths:
mycompany-terraform-state/├── production/terraform.tfstate ← default workspace└── env:/ ├── staging/production/terraform.tfstate └── dev/production/terraform.tfstateWith Terraform Cloud, each workspace is a fully separate entity with its own variables, state, and run history.
Using terraform.workspace in Configuration
The terraform.workspace expression returns the current workspace name:
# Vary resource configuration per workspacelocals { env = terraform.workspace
instance_type = { default = "t3.micro" staging = "t3.small" production = "t3.large" }
instance_count = { default = 1 staging = 2 production = 5 }}
resource "aws_instance" "app" { instance_type = local.instance_type[local.env] count = local.instance_count[local.env]
tags = { Environment = terraform.workspace Name = "app-${terraform.workspace}-${count.index + 1}" }}
resource "aws_db_instance" "main" { instance_class = local.env == "production" ? "db.r6g.large" : "db.t3.micro" multi_az = local.env == "production" deletion_protection = local.env == "production"
identifier = "myapp-${terraform.workspace}"}Workspace-Specific tfvars
Combine workspaces with environment-specific tfvars:
# Apply with workspace-specific variable fileterraform workspace select productionterraform apply -var-file="envs/${terraform.workspace}.tfvars"
# Or use a script to handle the workflowdeploy() { local env=$1 terraform workspace select "$env" || terraform workspace new "$env" terraform plan -var-file="envs/${env}.tfvars" -out="${env}.tfplan" terraform apply "${env}.tfplan"}
deploy productioninstance_type = "t3.large"instance_count = 5enable_ha = true
# envs/staging.tfvarsinstance_type = "t3.small"instance_count = 2enable_ha = falseTF_WORKSPACE Environment Variable
Set the workspace without running terraform workspace select:
# Use in CI/CDexport TF_WORKSPACE=productionterraform plan # Uses production workspace automatically
# GitHub Actions- name: Terraform Plan env: TF_WORKSPACE: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }} run: terraform planWorkspace Limitations
Workspaces share the same backend configuration and provider configuration. They’re a state isolation mechanism, not a full environment isolation mechanism.
Limitations:
- All workspaces use the same provider version and configuration
- Resources that must be globally unique (S3 bucket names, IAM role names) need naming conventions to stay unique across workspaces
- Backend access control is per-backend, not per-workspace — anyone with state access can access all workspaces
- Complex per-environment configuration leads to many conditional expressions
When to Use Directories Instead
Workspaces work best for environments that are structurally identical with minor configuration differences. For larger teams or significant environment differences, separate directories are often clearer:
infrastructure/├── environments/│ ├── dev/│ │ ├── main.tf ← calls shared modules│ │ ├── variables.tf│ │ └── terraform.tfvars│ ├── staging/│ │ ├── main.tf│ │ ├── variables.tf│ │ └── terraform.tfvars│ └── production/│ ├── main.tf│ ├── variables.tf│ └── terraform.tfvars└── modules/ ├── networking/ └── application/Use workspaces when:
- Environments are nearly identical (same resources, different sizes)
- Small team, simple access control
- Quick prototyping of multiple environments
Use separate directories when:
- Environments have meaningfully different architectures
- Different teams manage different environments
- You need per-environment state access control
- Production has resources (like extra monitoring, compliance) that dev doesn’t