Skip to main content
Terraform Workspaces: Manage Dev, Staging, and Production Environments

Terraform Workspaces: Manage Dev, Staging, and Production Environments

Key Takeaway

Learn Terraform workspaces to manage dev, staging, and production with the same code. Covers workspace commands, terraform.workspace variable, remote backends, and when NOT to use workspaces.

Table of Contents

What Are Terraform Workspaces?

Workspaces let you use the same Terraform configuration to manage multiple environments — each with its own state file.

Same .tf files → different state files → different infrastructure

workspace "dev"  → terraform.tfstate.d/dev/terraform.tfstate   → dev infra
workspace "staging" → terraform.tfstate.d/staging/terraform.tfstate → staging infra
workspace "prod" → terraform.tfstate.d/prod/terraform.tfstate → prod infra

Instead of duplicating code into dev/, staging/, prod/ directories, workspaces let you switch environments with one command.

Quick Start

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

# Switch to dev
terraform workspace select dev
terraform apply

# Switch to prod
terraform workspace select prod
terraform apply

Each apply creates separate infrastructure managed by separate state.

Workspace Commands

CommandDescription
terraform workspace listList all workspaces
terraform workspace showShow current workspace
terraform workspace new NAMECreate a new workspace
terraform workspace select NAMESwitch to a workspace
terraform workspace delete NAMEDelete a workspace
$ terraform workspace list
  default
  dev
* staging
  prod

The * marks the current workspace.

Using terraform.workspace in Configuration

The terraform.workspace variable returns the current workspace name. Use it to vary configuration per environment:

Instance Sizing

resource "aws_instance" "app" {
  ami           = var.ami_id
  instance_type = terraform.workspace == "prod" ? "t3.large" : "t3.micro"

  tags = {
    Name        = "app-${terraform.workspace}"
    Environment = terraform.workspace
  }
}

Variable Maps per Environment

locals {
  instance_type = {
    dev     = "t3.micro"
    staging = "t3.small"
    prod    = "t3.large"
  }

  instance_count = {
    dev     = 1
    staging = 2
    prod    = 3
  }
}

resource "aws_instance" "app" {
  count         = local.instance_count[terraform.workspace]
  ami           = var.ami_id
  instance_type = local.instance_type[terraform.workspace]

  tags = {
    Name        = "app-${terraform.workspace}-${count.index}"
    Environment = terraform.workspace
  }
}

Resource Naming

resource "aws_s3_bucket" "data" {
  bucket = "myapp-data-${terraform.workspace}"
}

resource "aws_db_instance" "main" {
  identifier = "myapp-db-${terraform.workspace}"
  # ...
}

Each workspace creates uniquely-named resources: myapp-data-dev, myapp-data-staging, myapp-data-prod.

Workspaces with Remote Backends

Workspaces work with all remote backends. Each workspace gets its own state file:

S3 Backend

terraform {
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "app/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-locks"

    # Workspaces store state at:
    # app/env:/dev/terraform.tfstate
    # app/env:/staging/terraform.tfstate
    # app/env:/prod/terraform.tfstate
  }
}

S3 automatically prefixes the key with env:/WORKSPACE_NAME/.

Terraform Cloud

terraform {
  cloud {
    organization = "my-org"
    workspaces {
      tags = ["app"]
    }
  }
}

In Terraform Cloud, workspaces are a first-class concept with their own variables, runs, and permissions.

Complete Example: Multi-Environment VPC

# variables.tf
variable "ami_id" {
  type = string
}

locals {
  config = {
    dev = {
      vpc_cidr       = "10.0.0.0/16"
      instance_type  = "t3.micro"
      instance_count = 1
    }
    staging = {
      vpc_cidr       = "10.1.0.0/16"
      instance_type  = "t3.small"
      instance_count = 2
    }
    prod = {
      vpc_cidr       = "10.2.0.0/16"
      instance_type  = "t3.large"
      instance_count = 3
    }
  }

  env = local.config[terraform.workspace]
}

# main.tf
resource "aws_vpc" "main" {
  cidr_block = local.env.vpc_cidr

  tags = {
    Name        = "vpc-${terraform.workspace}"
    Environment = terraform.workspace
  }
}

resource "aws_subnet" "app" {
  vpc_id     = aws_vpc.main.id
  cidr_block = cidrsubnet(local.env.vpc_cidr, 8, 1)

  tags = {
    Name = "app-subnet-${terraform.workspace}"
  }
}

resource "aws_instance" "app" {
  count         = local.env.instance_count
  ami           = var.ami_id
  instance_type = local.env.instance_type
  subnet_id     = aws_subnet.app.id

  tags = {
    Name        = "app-${terraform.workspace}-${count.index}"
    Environment = terraform.workspace
  }
}

# outputs.tf
output "vpc_id" {
  value = aws_vpc.main.id
}

output "instance_ips" {
  value = aws_instance.app[*].private_ip
}

Deploy:

terraform workspace select dev
terraform apply -var="ami_id=ami-12345678"

terraform workspace select prod
terraform apply -var="ami_id=ami-12345678"

Workspaces vs Directory Structure

Two approaches to managing multiple environments:

Workspaces Approach

project/
├── main.tf
├── variables.tf
├── outputs.tf
└── terraform.tfstate.d/
    ├── dev/
    ├── staging/
    └── prod/

Pros: No code duplication, easy to switch, consistent config. Cons: Same code must work for all environments, easy to apply to wrong workspace.

Directory Approach

project/
├── modules/
│   └── app/
├── dev/
│   ├── main.tf
│   └── terraform.tfvars
├── staging/
│   ├── main.tf
│   └── terraform.tfvars
└── prod/
    ├── main.tf
    └── terraform.tfvars

Pros: Clear separation, different configs, harder to accidentally apply to wrong env. Cons: Code duplication, harder to keep in sync.

When to Use Workspaces

  • Environments are similar (same resources, different sizes)
  • Small to medium projects
  • Solo developer or small team
  • Same AWS account for all environments

When to Use Directories

  • Environments are different (prod has extra services, monitoring, etc.)
  • Strict access control per environment
  • Different AWS accounts per environment
  • Large teams with separate responsibilities
  • Need different Terraform versions per environment

Safety Tips

Don’t Forget Which Workspace You’re In

Add the workspace to your shell prompt:

# In ~/.bashrc or ~/.zshrc
export PS1="\$(terraform workspace show 2>/dev/null) \w $ "

Or check before every apply:

echo "Current workspace: $(terraform workspace show)"
terraform plan

Protect Production

# Prevent accidental destroy in prod
resource "aws_db_instance" "main" {
  # ...
  lifecycle {
    prevent_destroy = terraform.workspace == "prod" ? true : false
  }
}

Note: prevent_destroy doesn’t support expressions directly. Use a variable instead:

variable "protect_resources" {
  type    = bool
  default = false
}

resource "aws_db_instance" "main" {
  # ...
  lifecycle {
    prevent_destroy = true  # Set per-workspace via tfvars
  }
}

Use Workspace-Specific Variable Files

terraform apply -var-file="${terraform.workspace}.tfvars"

# Or in a script:
terraform apply -var-file="$(terraform workspace show).tfvars"

Common Errors

“Workspace does not exist”

Error: Currently selected workspace "staging" does not exist.

Create it first: terraform workspace new staging

“default” workspace can’t be deleted

The default workspace always exists and cannot be deleted. Switch to a named workspace instead.

State conflicts between workspaces

Each workspace has isolated state. If you see resources from another workspace in your plan, check that you’re in the correct workspace: terraform workspace show.

Hands-On Courses

Learn by doing with interactive courses on CopyPasteLearn:

Conclusion

Terraform workspaces let you manage dev, staging, and prod with the same .tf files and separate state. Use terraform.workspace to vary instance sizes, counts, and naming. Use workspaces when environments are similar; use separate directories when they’re fundamentally different. Always check terraform workspace show before applying to production.

🚀

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.