Environments: directories vs workspaces

Terraform · Practical · 10 min

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-142

Remember

  • 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.

Check yourself

  1. Which approach most easily gives production a different set of resources (e.g. an extra WAF) than dev?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: developer.hashicorp.com/terraform/language/state/workspaces