If you find yourself having to choose between terraform workspaces for single codebase applied to dev, stage and prod environments vs for example another popular choise is to use module(s) + seperate folders per environment.
NOTE: The below part of this article is copied from AI. It does a good job of explaining the challenge and options. And for limiting some of the risks with workspaces read my previous article.
Comparing subfolders with shared modules versus Terraform workspaces is (such as dev, staging, and production).
1. Subfolders + Shared Modules (Directory-Based)
In this pattern, you maintain separate environment folders (/env/dev, /env/prod) that point to reusable infrastructure code stored in a central /modules directory. [1, 2]
How it Works Code Structure:
├── modules/ │ └── vpc/ │ ├── main.tf │ ├── variables.tf │ └── outputs.tf ├── envs/ │ ├── dev/ │ │ ├── main.tf (calls ../../modules/vpc with dev vars) │ │ └── backend.tf │ └── prod/ │ ├── main.tf (calls ../../modules/vpc with prod vars) │ └── backend.tfState Isolation: Each environment folder has its own distinct state file and backend configuration (e.g., separate keys or separate S3 buckets/GCS buckets). [1, 2]
Advantages:
- Clear Visibility: You can see what is deployed in prod versus dev directly in your file tree and pull requests. [1, 2]
- Blast Radius Protection: State files are completely separate. A corrupt state or accidental apply in dev will not touch prod state. [1]
- Divergence Support: Allows environments to drift or have genuinely different architectures (e.g., a tiny single-node database in dev, a multi-AZ cluster in prod). [1]
- Granular Access Control: You can restrict IAM permissions so junior engineers can apply to dev but cannot touch prod buckets/directories. [1]
Disadvantages
- Boilerplate Overhead: You must write a wrapper main.tf, variables.tf, and backend setup for every single environment. [1]
- Upgrading Pain: Updating provider versions or module inputs may require updating every environment folder manually. [1]
2. Terraform Workspaces
Terraform workspaces allow you to use the exact same configuration files to manage multiple distinct state files using a CLI-driven namespace (terraform workspace new prod). [1, 2, 3] How it Works
- Code Structure: A single set of configuration files (main.tf, variables.tf) uses conditional logic or terraform.workspace interpolations (e.g., count = terraform.workspace == “prod” ? 3 : 1) to scale resources differently per workspace. [1, 2]
- State Isolation: All state files are stored in the same backend, separated under a .tfstate.d/ directory structure or remote workspace backend key. [1, 2, 3]
Advantages:
- Low Code Duplication: No need to copy-paste folder structures or re-declare wrapper modules for each new environment. [1]
- Quick Ephemeral Environments: Great for spinning up short-lived feature branch or QA sandboxes that mirror production closely. [1, 2, 3]
Disadvantages:
- High Human Error Risk: It is very easy to run terraform apply while accidentally sitting in the wrong workspace (prod instead of dev), leading to catastrophic changes.
- Shared Backend Risk: All state files live in the same backend. You cannot easily apply strict, separate IAM permissions or distinct secret storage boundaries per environment easily.
- Code Obscurity: You cannot easily tell what parameters differ between environments just by reading the Git repository; it relies heavily on hidden conditional logic (terraform.workspace). [1, 2, 3, 4]
| Feature | Subfolders + Modules | Workspaces |
|---|---|---|
| State File Isolation | Complete (separate state keys/buckets) | Shared backend (namespaces inside same store) |
| Code Dryness (DRY) | Moderate (uses shared modules, thin env wrappers) | High (exact same codebase) |
| Visibility & Auditing | High (obvious in git diffs and folder trees) | Low (hidden active workspace state in CLI) |
| Risk of Accidental Prod Changes | Low (isolated folders/credentials) | High (easy to forget workspace select) |
| Best Used For | Long-lived, distinct tiers (dev, staging, prod) | Ephemeral test, feature, or QA environments |
Industry Recommendation
- Use Subfolders + Modules for standard multi-tier infrastructure (dev, staging, prod). The safety, clear audit trails, and permission boundaries vastly outweigh the slight boilerplate overhead.
- Use Workspaces only for identical, short-lived, or dynamically created environments (like PR preview environments or temporary QA sandboxes) where strict production isolation is not required. [1, 2, 3, 4]