Environments: Workspaces vs Directories
Directory-per-environment is boring, explicit, and safe. Workspaces are slick but easy to fumble in prod.
Every team eventually asks: how do I run dev, staging, and prod from one codebase? There are two schools of thought, and the senior answer is usually the less clever one.
Directory per environment (recommended for prod)
Each environment gets its own folder with its own backend and its own .tfvars. It is verbose, but you can see which environment you are in, and a wrong cd is more obvious than a wrong workspace.
Workspaces
One codebase, multiple named states via terraform workspace. Elegant for ephemeral or per-developer stacks, but the current workspace is invisible in your files — run apply in the wrong one and you will have a bad day.
| Concern | Directories | Workspaces |
|---|---|---|
| Blast radius clarity | High | Low |
| Config drift between envs | Possible | Impossible |
| Best for | Long-lived prod | Ephemeral / preview |
Example
# Directory-per-environment layout
# envs/
# dev/ main.tf backend.tf dev.tfvars
# prod/ main.tf backend.tf prod.tfvars
cd envs/prod
terraform init
terraform apply -var-file=prod.tfvars
# --- OR, the workspace approach ---
terraform workspace new staging
terraform workspace select staging
terraform workspace show # ALWAYS confirm before apply
terraform applyWhen to use it
- A team uses separate directories (environments/dev, environments/prod) so a mistake in the dev directory cannot accidentally modify production state.
- An organization uses workspaces for short-lived feature branches but directory-per-environment for long-lived dev/staging/prod to get the best of both approaches.
- An engineer runs terraform apply in environments/staging first, observes the result, then repeats in environments/prod, never touching both in the same command.
More examples
Directory-per-environment layout
Each environment directory has its own backend key pointing to a separate state file, making blast radius per directory, not per workspace.
environments/
dev/
backend.tf # key = "dev/terraform.tfstate"
main.tf # module calls with dev vars
dev.tfvars
staging/
backend.tf # key = "staging/terraform.tfstate"
main.tf
staging.tfvars
prod/
backend.tf # key = "prod/terraform.tfstate"
main.tf
prod.tfvarsApply only one environment
Demonstrates running apply in each environment's own directory, ensuring no single command can affect more than one environment.
# Apply staging
cd environments/staging
terraform init
terraform apply -var-file=staging.tfvars
# Apply prod (separate directory, separate state)
cd ../prod
terraform init
terraform apply -var-file=prod.tfvarsWorkspace approach for feature branches
Uses a temporary workspace for a feature branch to get isolated state without creating a new directory.
# Create workspace for feature branch
terraform workspace new feature-dark-mode
# Deploy experimental resources into isolated state
terraform apply -var="feature_flag=true"
# Tear down and delete when done
terraform destroy -auto-approve
terraform workspace select default
terraform workspace delete feature-dark-mode
Discussion