Every team must decide how environments are isolated; the wrong choice risks applying dev changes to prod.
Each environment needs its own state. Two common approaches:
Directory per environment (most common for long-lived environments): envs/dev, envs/staging, envs/prod, each a small root module calling shared modules with its own backend key and .tfvars. Pros: explicit, environments can differ (prod has a bigger database, dev skips the WAF), and different credentials or approval rules per directory are easy. Cons: some repetition in the root files.
CLI workspaces: one configuration, several named states. terraform workspace new staging, workspace select prod, and terraform.workspace in expressions. With the S3 backend, non-default workspaces live under the env:/ prefix. Pros: no duplication, great for short-lived copies (a stack per feature branch). Cons: it's easy to run in the wrong workspace, all environments share the same code version and backend credentials, and differences become conditionals on terraform.workspace.
Whatever you choose:
- Also split by component (network, data, apps) so each state stays small, plans stay fast, and the blast radius of a mistake is limited.
- Use separate cloud accounts or projects per environment where possible; it's the strongest isolation.
- Make the target environment obvious in CI job names and plan output.
HCP Terraform "workspaces" are a different, richer concept: one per state, with its own variables and permissions.
Example
# Layout A: directory per environment
# envs/
# dev/main.tf module "app" { source = "../../modules/app" size = "small" }
# prod/main.tf module "app" { source = "../../modules/app" size = "large" }
# each with backend key ".../dev/terraform.tfstate" or ".../prod/..."
# Layout B: CLI workspaces
terraform workspace new pr-142
terraform workspace list
# default
# * pr-142
terraform apply -var-file=preview.tfvars # name resources with terraform.workspace
terraform destroy -auto-approve && terraform workspace select default \
&& terraform workspace delete pr-142Remember
- Directory per environment: explicit and flexible; workspaces: same code, several states
- Split states by component to limit blast radius
- Separate accounts/projects per environment is the strongest isolation
Watch out for
- Running apply in the prod workspace while thinking you were in dev.