Terraform Workspaces for Environments
Manage multiple environments with Terraform workspaces. Learn how to create, switch, and use workspaces to isolate state and variables for dev, staging, and production.
Focus: manage multiple environments with workspaces
You've probably been here: you copy a main.tf into a prod/ folder, tweak a few variables, and hope you remember to run terraform apply in the right directory. Then one day you forget to switch, and your dev database gets replaced by a prod configuration. This is exactly the pain that Terraform workspaces solve. Instead of duplicating code and juggling directories, workspaces let you manage dev, staging, and production environments from a single configuration, with isolated state files and separate variable values. By the end of this lesson, you'll be able to manage multiple environments with workspaces confidently — creating, switching, and using them to keep your infrastructure safe and your sanity intact.
The problem this lesson solves
Managing multiple environments — dev, staging, prod — is a classic infrastructure headache. Here's what goes wrong without a deliberate strategy:
- Copy-paste drift: You duplicate your configuration into
dev/andprod/folders. Then you fix a bug indev/, but forget to apply the same fix toprod/. Over weeks, environments drift apart, and deployments become a game of whack-a-mole. - State collision: If you use the same Terraform state file for two environments,
terraform applyin one can destroy or modify resources in the other. This is the fast path to a production outage. - Variable spaghetti: You scatter
dev.tfvarsandprod.tfvarseverywhere, and every command needs a-var-fileflag. Errors creep in when you pass the wrong file. - Fear of switching: Because everything is tangled, you're scared to run
terraform planin your production directory. You cross your fingers and hope the diff is small — but it never is.
Workspaces directly attack this problem. They give you named, isolated state files for the same configuration, plus a clean way to vary inputs per environment. You stop duplicating code and start managing environments as first-class citizens of one codebase.
Core concept / mental model
Think of a Terraform workspace as a separate folder for state, but without copying your code.
In your current directory, you have a single configuration — main.tf, variables.tf, outputs.tf. Without workspaces, Terraform stores the state in a single file (or a single key in your remote backend). With workspaces, you get multiple state files, all named after the workspace, but sharing the same configuration.
Here's a mental picture:
- Your configuration is a template (the code).
- Each workspace is a filled-in copy of that template, with its own state and often its own variable values.
- When you run
terraform apply, Terraform knows which workspace you're in and reads/writes only that workspace's state.
This means you can have:
devworkspace —instance_count = 1,env = "dev"stagingworkspace —instance_count = 2,env = "staging"prodworkspace —instance_count = 5,env = "prod"
All from the same main.tf. The key to making this work is the terraform.workspace variable — a special value that always stores the name of the current workspace. You use it in your configuration to vary resource names, tags, and other values.
Definitions:
- Workspace: A named container that holds its own state file. Terraform automatically creates a
defaultworkspace when you start. terraform.workspace: A built-in variable that resolves to the current workspace name. Great for naming resources uniquely.- Backend: Where your state lives (local file or remote like S3). Each workspace gets its own state key inside that backend.
Pro tip: Workspaces are not a silver bullet. For complex environments (especially in teams), you might prefer directory-based environments with separate backends (e.g.,
envs/dev/,envs/prod/). We'll compare them in the next section.
How it works step by step
Here's the logical flow of using workspaces:
- Initialize your configuration with
terraform init. This sets up the backend (local or remote). - Create a workspace with
terraform workspace new dev. - Switch to a workspace with
terraform workspace select dev. - Provide environment-specific values — usually via
terraform.tfvarsfiles or CLI variables. You can even use the workspace name to conditionally set values. - Run
terraform planandapplyas usual. Terraform writes state to the workspace-specific backend location.
Cause → effect: When you switch workspaces, Terraform changes the state file path (in a remote backend) or the state filename (in local mode). The configuration code stays identical, but the state is isolated. This prevents cross-environment contamination.
Variable handling: Unlike state, variables are not automatically isolated per workspace. You need to explicitly use terraform.workspace or pass different variable files. A common pattern is to create dev.tfvars and prod.tfvars and run:
terraform apply -var-file="prod.tfvars"
In your code, you can also use conditional expressions based on terraform.workspace:
instance_type = terraform.workspace == "prod" ? "t3.large" : "t3.micro"
This is powerful but be careful: embedding too much logic in HCL can make your code hard to read. Prefer variable files where possible.
Hands-on walkthrough
Let's put this into practice with a simple but complete example: a set of AWS EC2 instances (or you can adapt to any provider).
Step 1: Set up your configuration
Create a directory and add these files:
# main.tf
provider "aws" {
region = "us-east-1"
}
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = terraform.workspace == "prod" ? "t3.large" : "t3.micro"
tags = {
Name = "web-${terraform.workspace}"
Env = terraform.workspace
}
}
# variables.tf
variable "instance_count" {
description = "Number of instances to launch"
type = number
}
# dev.tfvars
instance_count = 1
# prod.tfvars
instance_count = 3
Step 2: Initialize and create workspaces
terraform init
terraform workspace new dev
terraform workspace new prod
terraform workspace list
Expected output:
*
default
dev
prod
The * indicates the current workspace (default). Now select dev:
terraform workspace select dev
Step 3: Apply in the dev workspace
terraform apply -var-file="dev.tfvars" -auto-approve
You'll see output showing one instance with name web-dev.
Step 4: Switch to prod and apply
terraform workspace select prod
terraform apply -var-file="prod.tfvars" -auto-approve
This time, three instances named web-prod appear — and they are distinct from the dev ones.
Step 5: Verify state isolation
terraform state list
In the prod workspace, only prod resources appear. Switch back to dev and run terraform state list — you'll see the dev resource list. The states are completely separate.
Step 6: Clean up
terraform workspace select prod
terraform destroy -auto-approve
terraform workspace select dev
terraform destroy -auto-approve
terraform workspace delete dev
terraform workspace delete prod
Always destroy resources in a workspace before deleting it, otherwise the state lingers.
Pro tip: Use
terraform workspace showto display the current workspace name in scripts or CI. For example:if terraform workspace show | grep -q prod; then echo "Careful — prod!"; fi
Compare options / when to choose what
Workspaces are not the only way to handle multiple environments. Here's a comparison with the directory-based approach and multiple backends:
| Approach | Pros | Cons | Best for |
|---|---|---|---|
| Workspaces | Single config, easy to switch, state isolation built-in | Variable handling can be messy with many environments; state locking still per-workspace | Small teams, rapid prototyping, simple environments |
Directory-based (envs/dev, envs/prod) |
Full separation of state and code, can have different module versions, clearer variable files | Duplication of code or heavy use of modules; more directories to manage | Large teams, complex environments, stricter compliance |
| Multiple backends (separate S3 buckets) | Complete isolation, per-environment permissions | Requires more setup, still one config or copies | Enterprise with strong security requirements |
When to choose what:
- Choose workspaces when you want to quickly create and test environments with minimal overhead — ideal for single-person projects or CI/CD pipelines that spin up temporary environments.
- Choose directory-based when your environments have diverged significantly — e.g., prod uses a different module version or has extra resources — or when you have a large team that needs separate git reviews.
Workspaces are a great default for this lesson, but remember they are a tool, not a law.
Troubleshooting & edge cases
Issue: "Workspace already exists" when creating
Error: Workspace name already exists
You probably ran terraform workspace new dev twice. Fix: use terraform workspace list to see existing names, then terraform workspace select dev to switch.
Issue: "Error loading state: no such file or directory"
This happens when you try to apply in a workspace that hasn't been created yet. Fix: always create a workspace before applying. Use terraform workspace new <name> first.
Issue: Variables are not changing between workspaces
You set instance_type based on terraform.workspace, but the plan still shows the same value. Check: are you sure you're in the right workspace? Run terraform workspace show. Also confirm you're using terraform.workspace correctly — it's a string, so use quotes when comparing ("prod").
Edge case: Forgetting to destroy resources before deleting a workspace
If you run terraform workspace delete dev while dev still has resources, Terraform will orphan them (they remain in your cloud account but are no longer managed). This can cause unexpected costs. Fix: always terraform destroy before deleting a workspace.
Edge case: Remote backend and state locking
If you use an S3 backend, each workspace gets a distinct key (e.g., path/to/mykey/env:/dev). This is automatic, but ensure your IAM permissions allow write access to all relevant keys. Otherwise you'll get permission errors when switching workspaces.
Edge case: terraform.workspace in resource names
Resource names that include the workspace name (like aws_instance.web_dev) are fine, but if you change the workspace name, the resource will be recreated because the name changed. Plan carefully — use tags instead of resource names for identity.
What you learned & what's next
You've now learned to manage multiple environments with workspaces — a key skill for any Terraform professional. You can:
- Explain why workspaces solve the state-isolation problem.
- Create, list, and switch between workspaces.
- Use
terraform.workspaceto vary resource attributes per environment. - Pass environment-specific variable files.
- Destroy and delete workspaces cleanly.
This is a huge step toward writing production-grade Terraform. The next lesson in this track will show you how to combine workspaces with remote backends to lock state and enable collaboration in a team. You'll learn to enable state locking with DynamoDB and share state across a team — a natural follow-up to your new workspace skills.
Final pro tip: Treat workspaces as a convenience, not a magic wand. Always run
terraform planafter switching workspaces to catch surprises before you apply. Your future self — and your teammates — will thank you.
Practice recap
Now practice: create a small Terraform config for a simple resource (e.g., a null_resource or a local file). Create dev and prod workspaces, use terraform.workspace to name an output, and apply in both. Then destroy both and delete the workspaces. This will solidify the workflow before moving to remote backends.
Common mistakes
- Forgetting to run
terraform workspace selectbeforeapply, so you accidentally modify the wrong environment. - Deleting a workspace without running
terraform destroyfirst, leaving resources orphaned and incurring costs. - Hardcoding values in the configuration instead of using
terraform.workspaceor variable files, causing identical resource names across environments. - Assuming variables are automatically isolated per workspace — they aren't. You must pass the correct
-var-fileor use conditional logic.
Variations
- Directory-based environments: use separate folders like
envs/devandenvs/prod, each with its own backend and state file. - Remote backends with a key pattern that includes the environment name (e.g.,
state/dev.tfstate) to achieve isolation without workspaces. - Using
terraform.workspacefor naming tags, but you can also use a custom variable that you set manually to keep code simpler.
Real-world use cases
- A CI/CD pipeline that creates a temporary workspace for each feature branch, runs
terraform apply, then destroys and deletes it. - A small team managing dev and staging with a single repo, using workspaces to quickly apply new module versions in staging first.
- A solo developer running a personal project with separate environments for testing and production, keeping cost low and code simple.
Key takeaways
- Workspaces isolate state for the same configuration, preventing cross-environment conflicts.
- Create and switch workspaces with
terraform workspace newandterraform workspace select. - Use
terraform.workspaceand variable files to vary resource attributes per environment. - Always destroy resources before deleting a workspace to avoid orphaned infrastructure.
- Workspaces are best for simple, small-team needs; complex setups may prefer directory-based environments.
- Run
terraform planafter switching workspaces to catch unintended changes.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.