
Fact checked by Ryan Fee on June 3, 2025.
Terraform defines and provisions infrastructure as code. As projects grow, you end up with multiple configurations: one for dev, staging, and prod, or one per microservice. Keeping them straight gets messy. And cd-ing into the right directory before every terraform apply is easy to get wrong, especially in an automated pipeline where a missed directory change runs the command in the wrong place. The -chdir flag is built for exactly this.
terraform -chdir?Introduced back in Terraform v0.14.0, -chdir=<PATH> is a global option that tells Terraform to switch to a specific directory before running the command you give it (like init, plan, or apply). Your shell's current directory stays put, but Terraform operates as if it's in the directory you pointed it to.
The basic syntax is:
terraform -chdir=<PATH_TO_CONFIG_DIRECTORY> <SUBCOMMAND>For example:
terraform -chdir=environments/production applyThis command would run terraform apply using the configuration files located in the environments/production subdirectory.
-chdir?The point of -chdir is to make working with more than one Terraform setup less tedious.
-chdir lets you target them without manually navigating.cd commands anymore. This makes your scripts cleaner and less likely to break if a cd fails or isn't undone properly.It's not quite the same as just doing cd DIR && terraform <command>. A key difference is how Terraform sees paths. With -chdir, path.cwd (current working directory for Terraform) still points to where you ran the command from, while path.root points to the directory you specified with -chdir. This can be useful for configurations that need to reference files outside their own directory but within the broader project.
-chdirLet's look at a few common scenarios.
This is a big one. You probably have different setups for development, staging, and production. A common project structure might be:
my-project/
├── environments/
│ ├── dev/
│ │ └── main.tf
│ ├── staging/
│ │ └── main.tf
│ └── production/
│ └── main.tf
└── modules/
└── ...
From the my-project/ root, you can manage each environment:
# Initialize development
terraform -chdir=environments/dev init
# Plan changes for production
terraform -chdir=environments/production plan
# Apply changes to staging
terraform -chdir=environments/staging applyEach of these environment directories can have its own backend configuration, provider versions, and variable files, offering strong isolation. This is often preferred over CLI workspaces when backend configurations or even provider versions need to differ significantly between environments.
If your system is broken into smaller pieces (VPC, DNS, app services, databases), -chdir helps manage them independently.
my-infra/
├── vpc/
│ └── main.tf
├── dns/
│ └── main.tf
├── app-service-a/
│ └── main.tf
└── database-main/
└── main.tf
You can then target specific components:
terraform -chdir=my-infra/vpc apply
terraform -chdir=my-infra/app-service-a planThis modularity is great for larger teams or when you want to limit the blast radius of changes.
In a pipeline, -chdir keeps each step's target directory explicit.
# Example CI/CD pipeline steps
jobs:
deploy-dev:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
- name: Init Dev VPC
run: terraform -chdir=environments/dev/vpc init
- name: Apply Dev VPC
run: terraform -chdir=environments/dev/vpc apply -auto-approve
deploy-prod-app:
runs-on: ubuntu-latest
needs: deploy-dev # Example dependency
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
- name: Init Prod App
run: terraform -chdir=environments/prod/app init
- name: Plan Prod App
run: terraform -chdir=environments/prod/app planNo more cd some/path && terraform apply && cd ../.. gymnastics. Each step is explicit about its target. However, as the number of these -chdir targets grows, managing the orchestration, variable injection, and ensuring consistent execution across all of them in a CI/CD system can introduce its own layer of complexity.
-chdir vs. CLI WorkspacesTerraform CLI workspaces also let you manage multiple states from a single configuration. So which one fits your case?
-chdir): Better when environments need different backend configurations, different provider versions, or have significantly different infrastructure. This provides stronger isolation.Here’s a quick comparison:
| Feature | Directory-Based with -chdir | CLI Workspaces |
|---|---|---|
| Backend Configuration | Can be entirely separate per directory (different types, accounts, credentials). | Shared by all workspaces within the same working directory configuration. |
| State File Location | Each directory has its own terraform.tfstate (or remote equivalent) and .terraform dir. |
Separate state files per workspace (e.g., terraform.tfstate.d/<workspace_name>/terraform.tfstate for local backend) within one config dir. |
| Provider Configuration | Can be separate per directory (different versions, aliases). | Shared by all workspaces. |
| Code Duplication | Higher risk if not using modules effectively; configurations are physically separate. | Lower; uses the same set of .tf files, with variations via terraform.workspace or input variables. |
| Isolation Level | Stronger; physical separation of configuration, state, and backend. | Weaker; all workspaces share the same backend configuration and often the same credentials. |
| Ideal Use Cases | Managing distinct environments (dev, prod) with different backends/credentials, complex components. | Managing minor variations of the same infrastructure (e.g., feature branches, parallel test environments) with a shared backend. |
The -chdir approach gives you better isolation for distinct backends. The cost is that you now have many separate configurations to track, each with its own state and variables, and that tracking gets harder as the team grows.
-chdir and Backend/Provider/Module Managementbackend.tf) inside the -chdir directory. The .terraform directory (with plugin caches and backend info) is also created and managed there. This is key for isolation.-chdir target directory gets its own cache of provider plugins and modules in its .terraform subdirectory. This can mean downloading the same provider multiple times. To avoid this, you can set up a global plugin_cache_dir in your Terraform CLI configuration file (terraform.rc or terraform.cfg). This is a good idea for performance, especially in CI.source Paths: Relative module source paths (e.g., source = "../../shared_modules/network") are resolved relative to the configuration file within the -chdir target directory.If you're using Terraform Cloud or Enterprise, the "Working Directory" setting in a workspace is basically the -chdir equivalent. TFC/TFE will "change to" that directory in your VCS repo before running Terraform commands. This is great because it means you can often keep your multi-directory project structure when moving to a managed Terraform service.
environments/<env_name>/, components/<component_name>/).-chdir is really built for scripts and CI/CD.path.root is the -chdir target, and path.cwd is where you ran the command.init: Run terraform -chdir=DIR init for each target directory first.plugin_cache_dir: It'll speed things up.A known issue can pop up with terraform init -chdir if you're using implicit filesystem mirrors for providers (like a terraform.d/plugins/ in your current directory). Terraform might get confused about where to find them. The workaround is often to move that mirror relative to the -chdir target, or better yet, rely on the official provider registry or explicit provider_installation blocks.
-chdir: Scaling ChallengesThe -chdir option beats navigating directories by hand. It brings clarity to local workflows and CI/CD scripts. But the work of managing those separate execution contexts doesn't go away as the number of environments, components, and teams grows. It moves around.
Once you're relying on -chdir plus custom scripting, a few things get hard at the same time: keeping execution consistent, handling variables and secrets safely across dozens of configurations, enforcing policies like tagging or security group rules, and seeing all of your infrastructure in one place. This is usually the point where teams start looking for a platform instead. Scalr builds on these same concepts as a centralized control plane for Terraform operations. It offers hierarchical variable management, role-based access control (RBAC) scoped to environments or components, automated policy enforcement (via OPA, for example), and cost estimation before changes are applied. Those are hard to build and maintain yourself on top of raw -chdir workflows.
terraform -chdir helps organize complex projects and makes automation cleaner. Once you know how it behaves with backends, workspaces, and modules, you can put it to work without surprises.
For a lot of teams, -chdir gives them the control and organization they need. But as an IaC practice scales, managing a pile of separate configurations gets complicated, and that's often when people start looking at platforms like Scalr for more abstraction, governance, and collaboration across the whole setup.
Sebastian Stadil, CEO and founder of Scalr, brings over 15 years of DevOps experience. His experience dates back to using AWS in 2004, even before the S3 public beta. He is also the program chair for OpenTofu at the Linux Foundation and previously advised Microsoft Azure and Google Cloud, serving a combined ten years on their respective Cloud Advisory Boards.
-chdir syntax and general global options)terraform.io/cli/commands#chdir-option, but developer.hashicorp.com is the current documentation portal. The -chdir details are typically integrated into the main global options page.)**terraform: Add -chdir global option**. (2020, October 28). HashiCorp/terraform. Retrieved from https://github.com/hashicorp/terraform/issues/26070**terraform init -chdir** fails with implicit filesystem mirror in CWD. (2021, May 20). HashiCorp/terraform. Retrieved from https://github.com/hashicorp/terraform/issues/28932