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.

ConcernDirectoriesWorkspaces
Blast radius clarityHighLow
Config drift between envsPossibleImpossible
Best forLong-lived prodEphemeral / preview

Example

Example · bash
# 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 apply

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

Example · bash
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.tfvars

Apply only one environment

Demonstrates running apply in each environment's own directory, ensuring no single command can affect more than one environment.

Example · bash
# 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.tfvars

Workspace approach for feature branches

Uses a temporary workspace for a feature branch to get isolated state without creating a new directory.

Example · bash
# 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

  • Be the first to comment on this lesson.