Workspaces & environments
dev, staging, prod from one codebase.
Almost every team needs the same infrastructure in several environments — dev, staging, prod — that differ only in size and a few settings. There are two established ways to do this in Terraform, and choosing well matters because the wrong one causes prod outages. The two are workspaces and directory-per-environment, and they make different tradeoffs about isolation.
Workspaces: same code, separate state
Workspaces let one configuration keep multiple independent state files — terraform workspace new staging, and the same code now has a separate state for staging. It is lightweight and good for near-identical, ephemeral environments (a per-branch preview). The catch: all workspaces share the same backend and code, and it is easy to run a command against the wrong workspace and change prod thinking you were in dev, because the only difference is invisible current context.
$ terraform workspace new staging$ terraform workspace select staging$ terraform workspace listdefault* staging # the * is your only clue which env you are about to change# reference it in code: count = terraform.workspace == "prod" ? 3 : 1
Directory (or Terragrunt) per environment
The more robust pattern for prod is a separate directory per environment — environments/dev, environments/prod — each with its own backend config and its own tfvars, usually calling shared modules for the actual resources. This gives strong isolation: prod has its own state, its own backend, and you must be physically in the prod directory to change it. It is more explicit and a little more repetitive, which tools like Terragrunt exist to DRY up.
modules/ # shared, reusable resource definitionsnetwork/ web/ db/environments/dev/main.tf backend.tf dev.tfvars # dev’s own state + valuesprod/main.tf backend.tf prod.tfvars # prod’s own state + values# to change prod you must cd into environments/prod — explicit and hard to fat-finger