Should you use Terraform workspaces or separate directories/state files to manage dev, staging, and production — what's the actual trade-off?
Short Answer
Workspaces are lightweight and keep configuration fully DRY (one set of .tf files, switched between named state instances) but make it easy to accidentally run a command against the wrong environment, since switching workspace is a separate, easy-to-forget step from running the actual command — separate directories per environment (each with explicit, distinct backend configuration) require more duplication but make "which environment am I about to affect" structurally unambiguous, since it's determined by which directory you're in, not an easy-to-overlook workspace selection. For production environments specifically, most teams find the explicit-directory approach's safety property worth the extra duplication.
Detailed Explanation
Workspaces share one configuration, differing only in which named state instance is currently selected: terraform workspace new staging, terraform workspace select production switch which state file a given command operates against, while the actual .tf configuration files remain identical across every workspace — this is genuinely convenient for keeping configuration DRY, since there's no duplicated code between environments at all.
The core risk: workspace selection is a separate, stateful, easy-to-forget step: running terraform apply doesn't itself indicate which workspace is currently selected in its own command — a terminal session left on the wrong workspace from earlier (forgotten context) means the next command runs against the wrong environment's state, with nothing about the command itself warning you — this is a genuine, real-world footgun, especially in a terminal session that's been open a while.
Separate directories make the target environment structurally explicit at the point of running any command: with a production/ directory and a staging/ directory each containing their own (even if largely similar) configuration and explicit backend block, which environment you're affecting is determined by which directory you're physically in when running the command — there's no separate, forgettable "selection" step; the environment is unambiguous from your current working directory.
Separate directories do mean genuine configuration duplication, which needs its own management strategy: keeping production/main.tf and staging/main.tf in sync (when they should be identical except for environment-specific variables) requires either careful discipline, a shared module both directories call (factoring the actual resource definitions into a common module, with each environment directory just calling it with different variables), or accepting some drift risk between environments' configurations over time.
The common, sensible pattern: use a shared module containing the actual resource definitions (avoiding logic duplication), with separate root-module directories per environment (each with explicit backend configuration and environment-specific variable values) calling that shared module — this captures workspaces' DRY benefit (the actual resource logic lives in one place, the module) while keeping separate directories' safety property (unambiguous target environment per directory), rather than choosing purely between the two original options.
Interview Follow-Up Questions
- How would you add a safeguard (a script check, a CI step) to confirm the correct workspace or directory is being targeted before a production apply, regardless of which pattern you use?
- What's the difference in how remote state backend configuration works between the workspaces approach and the separate-directories approach?
- How would you migrate an existing workspace-based setup to the separate-directories-with-shared-module pattern, without disrupting existing state?
Key Takeaways
- Workspaces keep configuration fully DRY but make workspace selection a separate, stateful, easy-to-forget step that can lead to running a command against the wrong environment.
- Separate directories make the target environment structurally unambiguous (determined by working directory), at the cost of configuration duplication.
- Most teams find the explicit-directory approach's safety property worth the extra duplication specifically for production environments, given the stakes of the workspace-selection footgun.
- A shared module (containing actual resource logic) called by separate per-environment root-module directories captures both DRY configuration and unambiguous environment targeting.
References
Last updated August 22, 2026 · Last reviewed August 22, 2026