Terraform
Intermediate 12 min read Terraform

Workspaces & Environments

Isolation is a design decision

Workspaces are per-state runtimes on one backend; environments are the separation of prod/stage/dev. Best practice: separate directories/backends (keys) per env and use workspaces for the rare same-config-different-state case, with per-env variables.

workspaceenvironmenttfvarsdirectorystate isolation
Prerequisites: Terraform Modules
Before we start

What you will learn

  • Distinguish workspaces from environment separation
  • Configure per-env tfvars and remote state keys
  • Reason about workspace pitfalls in teams
In 60 seconds

workspace = named state instance over one config. env = prod vs stage vs dev, usually separated by directory + remote backend key + tfvars. Workspaces are convenient; directories are the dependable mainline for state isolation.

Introduction

What is this?

Workspaces: terraform workspace new <name>; shared configuration, separate state; convenient for small same-shape deployments; limited by inability to vary provider config cleanly and 'workspaces are name-only seams' confusion. Environment separation approaches: directories (envs/prod, envs/stage, envs/dev) each with backend key, tfvars; mono-repo per env with modules; backend per env (per-team). Variables: -var-file, terraform.tfvars.json, *.auto.tfvars, TF_VAR_*. Env-specific knobs: counts conditionals, different SKUs. Gotcha: workspaces + shared resources + provider configs intersect badly across regions; always document the model.

Motivation

Why do you need it?

  • Prod must never see a dev tfvars mistake ride into apply.
  • Remote keys per env give clean blast-radius and rollback per stack.
  • Per-env variable files are a reviewable boundary between worlds.
  • Workspaces misused cause cross-env state collisions hidden until prod breaks.
Think about it

Mental model

Imagine

Two kitchens sharing one stove vs two separate kitchens

Think of

Workspaces vs env directories

Workspaces = one kitchen, two menus named; fine for small dinners, but grease (configuration) mixes. Separate kitchens (per-env dirs) keep steak night from burning in the pudding.

Mechanics

How it works

  • You choose the model before initializing: workspace or dir-key separation.
  • Backend key embeds env: envs/prod/site.tfstate vs envs/dev/site.tfstate.
  • Each env defines its variable set (tfvars/auto.tfvars) same schema.
  • CI applies the correct env only; plan/apply scoped by directory or -workspace.
  • Promotion between envs: same code, different vars, tested odd stages.

Environment isolation.

Same module tree
envs/prod
envs/stage
envs/dev
Backends
Hands-on

Examples

Per-env backend keys + tfvars

Per-env backend keys + tfvars
envs/prod/backend.tf → key = "vpc/prod.tfstate"
envs/stage/backend.tf → key = "vpc/stage.tfstate"
envs/dev/backend.tf → key = "vpc/dev.tfstate"
envs/prod/prod.tfvars → env = "prod", instance_type = "m5.large"
envs/dev/dev.tfvars → env = "dev", instance_type = "t3.small"
# apply: cd envs/prod && terraform apply -var-file=prod.tfvars

State isolation per env + variable boundaries per env, sharing one module tree — the dependable pattern.

Real world

In production

  • Mono-repo QA: three env directories each CI-deployable.
  • Workspace carve for a single-service shard where one config, N named states is truly correct.
  • Multi-team: each team's stack dir with its own backend + IAM role.
Guidelines

Best practices

  • Default to directories-per-env + remote keys; reach for workspaces only for identical small shards.
  • Name everything with the env (kubernetes clusters, buckets, contexts) to avoid collisions.
  • Use .auto.tfvars for default dev; explicit -var-file for prod in CI.
  • Lock prod state (narrow IAM) so human `destroy -auto-approve` can't casually nuke it.
  • Document the isolation model in the README; newer teammates will guess otherwise.
Watch out

Common mistakes

  • One workspace for everything → prod and dev sharing a config inevitably merge.
  • Per-env fork of the module tree → drift factory (update once, forget the third).
  • Missing -var-file → tfvars from the wrong environment silently applied.
  • Failure to namespace global names in shared accounts → creation conflicts.
When things break

Debugging tips

  • Resource created in wrong env → check current workspace + backend key; verify with state pull.
  • Apply silently skipping a var → verify which tfvars/auto-tfvars were loaded (CLI + cwd).
  • Promotion broken → compare module versions/locks across env dirs (drift in pin).
  • Workspace list vs backend state → reconcile names, one per logical env.
Interviews

Interview checkpoint

Test yourself

Quick quiz

0/2
1

The dependable way to isolate envs is:

2

Workspaces give you:

Go deeper

Official documentation