Skip to main content

Terraform Stacks vs Workspaces: When to Use Each

Key Takeaway

Terraform Stacks vs Workspaces compared. Understand when to use Workspaces for environment isolation vs Stacks for multi-component orchestration, with migration examples.

Table of Contents

Terraform Stacks (GA late 2025) and Workspaces solve different problems, but teams often confuse them. Workspaces isolate state for the same configuration. Stacks orchestrate multiple configurations as a single deployment. Here’s when to use each.

Core Difference in 30 Seconds

Workspaces: One Terraform configuration → multiple isolated state files (dev, staging, prod).

Stacks: Multiple Terraform configurations → one coordinated deployment (networking + database + compute).

Workspaces:
  main.tf ──→ dev.tfstate
          ──→ staging.tfstate
          ──→ prod.tfstate

Stacks:
  networking/main.tf ─┐
  database/main.tf   ─┼──→ coordinated apply/destroy
  compute/main.tf    ─┘

Side-by-Side Comparison

FeatureWorkspacesStacks
PurposeSame code, different environmentsMultiple codebases, one deployment
State filesOne per workspaceOne per component
DependenciesManual (remote_state data)Declared (component.X.output)
Apply orderYou decideAutomatic (dependency graph)
Destroy orderYou decide (reverse)Automatic
Multi-envterraform workspace selectDeployment targets in .tfdeploy.hcl
Requires HCPNo (CLI works)Yes (HCP Terraform only)
OpenTofu✅ Supported❌ Not available
ComplexityLowMedium
Best forEnvironment isolationMulti-component systems

Workspaces: Environment Isolation

How They Work

# Create workspaces
terraform workspace new dev
terraform workspace new staging
terraform workspace new prod

# Switch and apply
terraform workspace select dev
terraform apply   # Uses dev.tfstate

terraform workspace select prod
terraform apply   # Uses prod.tfstate (completely separate)

Typical Structure

project/
├── main.tf
├── variables.tf
├── outputs.tf
├── terraform.tfvars          # Default values
├── environments/
│   ├── dev.tfvars
│   ├── staging.tfvars
│   └── prod.tfvars
# main.tf — same code for all environments
resource "aws_instance" "web" {
  ami           = var.ami_id
  instance_type = var.instance_type  # t3.micro in dev, t3.xlarge in prod

  tags = {
    Name        = "${terraform.workspace}-web"
    Environment = terraform.workspace
  }
}
terraform workspace select prod
terraform apply -var-file=environments/prod.tfvars

When Workspaces Work Well

  • Same architecture, different sizes: Dev gets t3.micro, prod gets t3.xlarge
  • Same resources, different accounts: Deploy identical infra to dev/staging/prod AWS accounts
  • Feature environments: Spin up temporary environments for testing
  • Simple projects: Single root module, no cross-module dependencies

When Workspaces Break Down

# Problem: Networking team manages VPC, app team manages compute
# With workspaces, everything is in one configuration
resource "aws_vpc" "main" { ... }           # Networking team
resource "aws_ecs_cluster" "app" { ... }    # App team
resource "aws_rds_cluster" "db" { ... }     # Database team

# Issues:
# - One team's change can break another's
# - Long plan times (Terraform evaluates everything)
# - Blast radius is the entire stack
# - Different teams need different permissions

Stacks: Multi-Component Orchestration

How They Work

# stack.tfstack.hcl
component "networking" {
  source = "./components/networking"
  inputs = {
    environment = var.environment
    vpc_cidr    = var.vpc_cidr
  }
}

component "database" {
  source = "./components/database"
  inputs = {
    environment = var.environment
    vpc_id      = component.networking.vpc_id         # Auto-dependency
    subnet_ids  = component.networking.private_subnets
  }
}

component "compute" {
  source = "./components/compute"
  inputs = {
    environment = var.environment
    vpc_id      = component.networking.vpc_id
    db_endpoint = component.database.endpoint          # Auto-dependency
  }
}
# deployments.tfdeploy.hcl
deployment "dev" {
  inputs = {
    environment = "dev"
    vpc_cidr    = "10.0.0.0/16"
  }
}

deployment "prod" {
  inputs = {
    environment = "prod"
    vpc_cidr    = "10.1.0.0/16"
  }
}

When Stacks Work Well

  • Multi-team projects: Networking, database, and compute teams own separate components
  • Complex dependency chains: Database needs VPC, compute needs VPC + database
  • Coordinated destroy: Tear down compute → database → networking in correct order
  • Large infrastructure: 100+ resources split across logical components
  • Deferred changes: Component B can’t plan until Component A creates resources

When Stacks Are Overkill

  • Simple single-module projects
  • Projects that don’t need cross-component coordination
  • Teams not on HCP Terraform
  • Projects needing OpenTofu compatibility

The Decision Framework

Q: Do you have ONE Terraform configuration used in multiple environments?
   → Use WORKSPACES

Q: Do you have MULTIPLE Terraform configurations that deploy together?
   → Use STACKS

Q: Do you have both?
   → Use STACKS (they include deployment targets for multi-env)

Real-World Examples

ScenarioUse
Same web app deployed to dev/staging/prodWorkspaces
VPC + EKS + RDS deployed as a unitStacks
Per-developer preview environmentsWorkspaces
Platform team managing shared infra + app teams deploying on topStacks
Single Lambda function in 3 regionsWorkspaces
Microservices with shared networking and individual servicesStacks

Before Stacks: The Workspace + Remote State Pattern

Before Stacks existed, teams used workspaces + terraform_remote_state data sources:

# In the compute configuration:
data "terraform_remote_state" "networking" {
  backend = "s3"
  config = {
    bucket = "tf-state"
    key    = "networking/terraform.tfstate"
    region = "us-east-1"
  }
}

resource "aws_ecs_cluster" "app" {
  name = "app"
  # Reference networking output
  # But if networking hasn't been applied yet... this fails
}

resource "aws_ecs_service" "app" {
  cluster = aws_ecs_cluster.app.id
  network_configuration {
    subnets = data.terraform_remote_state.networking.outputs.private_subnets
  }
}

Problems with this approach:

  • No orchestration — you must apply networking first, manually
  • No coordinated destroy
  • Remote state references are fragile
  • No deferred changes

Stacks solve all of these.

Can You Use Both?

Yes — within a Stack, each component can use workspaces internally. But in practice, Stacks’ deployment targets replace the need for workspaces in most cases.

Hands-On Courses

Conclusion

Workspaces and Stacks solve fundamentally different problems. Use Workspaces when you have one configuration deployed to multiple environments. Use Stacks when you have multiple configurations that need coordinated deployment and destruction. For simple projects, Workspaces are sufficient and work everywhere (CLI, HCP, OpenTofu). For complex multi-component systems on HCP Terraform, Stacks eliminate the manual orchestration and remote state glue that teams have fought with for years.

🚀

Level Up Your Terraform Skills

Hands-on courses, books, and resources from Luca Berton

Luca Berton
Written by

Luca Berton

DevOps Engineer, AWS Partner, Terraform expert, and author. Creator of Ansible Pilot, Terraform Pilot, and CopyPasteLearn.