Why I Use Terragrunt Over Terraform/OpenTofu in 2025
Terragrunt vs Terraform: Why I chose Terragrunt to eliminate code duplication, automate state management, orchestrate deployments, and follow pattern-level reuse.
For managing multiple environments with Terraform, there are two possibilities:
Using folder duplication.
Using Terraform Workspaces.
Let’s start with the first option.
In this scenario, you would build your Terraform modules under the modules directory and create one directory per environment (dev, staging, and prod).
Each main.tf instantiates the vpc module. You can directly see the problem here: we need to redefine provider.tf, backend.tf, variables.tf, and outputs.tf in each environment folder.
That’s a lot of duplication! Any change to shared configuration needs to be manually copied to every environment.
Manual Backend State Setup
Following the example from the previous section, you would also need to write the backend configuration for each environment:
You will also need to create the S3 bucket manually before anyone can run terraform init.
Usually, this means running a separate Terraform configuration just for the backend infrastructure, or clicking through the AWS console.
The whole process becomes a headache that only gets worse as you add more environments and team members.
You got it right! Each time you need to create an additional environment, you’ll need to copy and paste an environment folder and manually change all the keys.
No Built-in Orchestration
Terraform CLI can only work on one directory at a time. It has no idea that your modules depend on each other.
Here’s a typical application stack:
dev/├── vpc/ # Must deploy FIRST├── database/ # Must deploy SECOND (needs VPC) ├── app-servers/ # Must deploy THIRD (needs database)└── load-balancer/ # Must deploy FOURTH (needs app servers)
To deploy this stack, you have to manually run commands in dependency order:
cd dev/vpc &&terraform applycd ../database &&terraform apply cd ../app-servers &&terraform applycd ../load-balancer &&terraform apply
Following such an error-prone process in a production environment is not realistic.
Many teams think CI/CD pipelines will elegantly solve these Terraform limitations. I don’t believe it, and I’ll explain you why.
You still need custom scripts to handle orchestration logic. CI/CD just moves the complexity into your YAML pipeline:
name: Deploy Infrastructurejobs:deploy:steps:-run: cd environments/dev/vpc && terraform apply -auto-approve-run: cd ../database && terraform apply -auto-approve -run: cd ../app-servers && terraform apply -auto-approve-run: cd ../load-balancer && terraform apply -auto-approve
Using this solution, we port the orchestration logic in YAML instead of using purpose-built tools.
Unfortunately, we still have all the problems we discussed earlier:
Copy-paste directory structures
Manual backend configuration
No native dependencies between modules
Here’s another tedious scenario: when adding a new module or changing the dependencies, you need to rewrite your custom orchestration scripts manually.
I think CI/CD is excellent for deployment automation, but it’s the wrong approach for solving the architectural flaws of Terraform.
We’re simply adding another layer of complexity on top of the already existing problems.
Terraform Workspaces Are Flawed
Terraform Workspaces let you create separate state files for the same codebase. Each workspace maintains its own state while sharing the same configuration files.
Many engineers use this feature as a way to maintain a single configuration and change the backend’s state file path according to the workspace.
HashiCorp themselves don’t recommend this practice in their official documentation:
“Workspaces alone are not a suitable tool for system decomposition because each subsystem should have its own separate configuration and backend.” (source)
Workspaces only change state file names. They don’t solve the architectural problems we’ve been discussing.
Additionally, you’re not able to make your environment configurations diverge. For example, you need to use the same instances across dev and prod. In realistic scenarios, you’ll want to create smaller machines during development.
It’s also easy to forget that you’re not in your dev workspace anymore and inadvertently affect your prod environment.
Terraform Cloud? Not Always the Solution
Terraform Cloud Pricing: resource ramp-up very quickly with multi-environment setups | source: HashiCorp
Terraform Cloud Pricing: resource ramp-up very quickly with multi-environment setups (source)
Terraform Cloud (TFC) is HashiCorp’s paid, proprietary platform. For teams focused on open-source tooling, this immediately rules it out.
HashiCorp has introduced Terraform Stacks to address some orchestration concerns.
Stacks can help manage dependencies between components and reduce some of the manual coordination we’ve been talking about.
However, Stacks is still in beta, has limited deployment options (500 resources max), and requires expensive tiers for full access.
Terraform Cloud adds collaboration features and a web UI.
From my experience, it makes sense to invest in TFC when collaboration becomes a real need, especially for large DevOps teams.
For teams with less than 10 engineers, I think sticking to Terragrunt is a sound move: you get the same orchestration benefits without the vendor dependency or per-resource costs.
Terragrunt for Production in 2025
I don’t get excited about many tools, but Terragrunt genuinely transformed how I manage Infrastructure as Code.
Terragrunt is a wrapper over Terraform that tackles its limitations.
After dealing with all the problems I just described, it solved every major pain point in a way that felt elegant and maintainable.
To be clear, the discussion is not really “Terragrunt (TG) vs Terraform (TF)” but rather “native TF vs (TF + TG)”. As TG uses TF under the hood.
Let’s jump right into how it addresses each issue we’ve covered.
The dev environment would be identical except env = "dev" and only one availability zone, for example azs = ["us-west-2a"].
include blocks let child configurations inherit everything from the parent. Terragrunt automatically generates the provider.tf file in each environment:
terraform { required_providers { aws = { source ="hashicorp/aws" version ="~> 5.0" } }}provider "aws" { region ="us-west-2"}
It prevents the duplication of the Terraform version, the AWS provider version, and the provider block.
Supports Automated Backend State Isolation
Remember the manual backend configuration nightmare from earlier? Terragrunt eliminates all of that.
You define the backend configuration once in your root.hcl file, and Terragrunt automatically handles unique state isolation for each environment:
It literally does the coffee for you: Terragrunt automatically creates the S3 bucket and DynamoDB table if they don’t exist.
No more bootstrap scripts or clicking manually on the AWS console!
The path_relative_to_include() function automatically generates unique S3 keys based on the directory structure.
Your prod environment produces prod/terraform.tfstate, while dev generates dev/terraform.tfstate. No manual key configuration, no copy-paste backend files!
Unlike Terraform’s backend blocks, Terragrunt can use dynamic values and local variables in backend configuration.
You write the backend setup once, and it works correctly across all environments without any manual intervention.
Has Built-in Orchestration
Remember the custom CI/CD scripts and manual orchestration from earlier? Terragrunt eliminates that entirely with native dependency management.
Unit: A Terragrunt wrapper around a Terraform module. Defines a single, deployable piece of infrastructure.
Stack: Defines a collection of related units that can be reused.
Making The Example Even DRYer
In the previous example, we still had to copy Terragrunt configurations in each environment directory. prod/app/terragrunt.hcl and dev/app/terragrunt.hcl were still duplicated.
With Terragrunt Stacks, we can factorize that too!
First, we will move prod/app/terragrunt.hcl and prod/database/terragrunt.hcl to the units directory without changing their content.
Next, we define the pattern (app + database) once in a stack file:
prod and dev are exactly identical. That’s right! Because our units search for the env.hcl file in parent directories, we don’t even have to specify the environment.
You can even point your dev stack to a more recent version v1.0.1 and keep prod on a stable one.
This is already a significant improvement. We’ve eliminated the last bits of duplication.
But honestly, I thought this was just a nice incremental upgrade.
I was wrong.
Nested Stacks
Nested Terragrunt Stacks example | source: Gruntwork
Here’s what I didn’t expect: Terragrunt Stacks can be nested.
For example, you could re-use your web-app stack and add a monitoring stack to it.
Gruntwork is planning to incorporate the ability to use stack outputs as dependencies in the future. This will allow for even greater modularity.
Then it clicked: this isn’t just about eliminating copy-paste anymore.
Pattern Level Re-Use
This is the fundamental shift I completely missed at first.
With this new feature, platform teams aren’t just sharing Terraform modules anymore. They’re packaging and distributing complete infrastructure patterns.
Want to deploy a new microservice? Don’t think about databases, load balancers, monitoring, and networking separately. Just reference the microservice pattern and input your application-specific values.
Need a data pipeline? Reference the data pipeline pattern. It comes with ingestion, processing, storage, and observability already wired together.
Imagine the possibilities: creating a reusable stack that you could easily incorporate into your client’s infrastructures.
I’ve never had performance issues deploying and maintaining multiple environments with thousands of resources.
Objection 3: Difficult Debugging
Coming from Terraform, people may find the debugging process more difficult because Terragrunt has longer traces. It applies the infrastructure following a Directed Acyclic Graph (DAG).
I think it is certainly easier to debug than the custom CI/CD workflows needed to work around Terraform’s caveats.
Why I Think These Trade-offs Are Worth It
Look, I’m not going to pretend Terragrunt has zero learning curve or that every team should adopt it.
But when I see a 2025 article from Scalr ranking #1 on Google for Terragrunt drawbacks, I’m baffled to find that all the highlighted problems have been resolved since 2021-2022.
Unfortunately, this leads people to make decisions based on outdated information.
At the time of the writing, Terragrunt has 215 open issues versus 2,350 closed issues. That’s pretty solid for an open-source project.
Bottom line: for teams managing multiple environments with orchestrated deployments, I think Terragrunt is absolutely worth it.
Terraform vs Terragrunt: Comparative Feature Matrix
Here’s my attempt at building a Terragrunt versus Terraform table:
Dimension
Terragrunt v0.80+
Terraform/OpenTofu
Workflow Simplicity
High: One command deploys an entire stack of modules defined in one file.
Low: Requires manual scripts or CI workflows to coordinate multiple modules.
Code Duplication
Low: Hierarchical configs via include blocks. Stacks eliminate copy-paste across environments. Modules reused without duplication.
High: Module reuse exists, but backend/provider configs must be duplicated.
Dependency Handling
Automatic: Built-in dependency graph with dependency blocks.
Manual: No inter-module automatic dependencies.
State Isolation
Strong: Enforces one state per module. Auto-calculates unique remote state keys. Can auto-create backends. Small blast radius.
Manual: State keys must be split manually. Large modules risk huge blast radius.
Performance
Moderate: v0.80 delivered 42% faster execution. Still adds overhead, but now acceptable.
Baseline: No wrapper overhead.
Testability
Moderate: Works with Terratest. Can capture plan outputs for validation. No built-in test framework.
High: Has provider mocking. Otherwise same testing approaches as Terragrunt.
Learning Curve
High: Must learn Terragrunt syntax. Initial training investment needed, but day-to-day usage becomes simpler.
Low: Most DevOps engineers are already familiar with Terraform.
Community
Moderate: Active, engaged community with responsive maintainers.
Low: Integration with Atlantis is not trivial. Digger seems like a promising OSS alternative. For enterprise features, Gruntwork Pipeline has been designed for Terragrunt.
High: Supported by most (if not all) TACOS platforms.
Terragrunt dominates features-wise (workflow, DRY, dependencies, state isolation) while Terraform/OpenTofu wins on familiarity, simplicity, performance, and TACOS support.
Final Takeaway
I’ll be honest, Terragrunt has genuinely made my infrastructure experience easier and more enjoyable.
When I read people complain about Terragrunt being “too complex” or “over-engineered,” I’ve realized they’re usually thinking about the 2019 version or relying on misconceptions.
What happened in 2025 changed the entire conversation.
Terragrunt evolved from a DRY orchestration tool to a pattern-level infrastructure platform. Meanwhile, Terraform remained focused on component-level management.
Even before Stacks, I used to rely on Terragrunt instead of Terraform for reducing duplication and orchestration. Now? They’re solving different problems entirely.
Terragrunt isn’t right for everyone. If you’re managing a single environment with few resources, native Terraform is probably fine. If you’re a small team that deploys infrastructure once a month, the learning curve might not be worth it.
But if you’re managing multiple environments, writing custom module orchestration, or constantly copying configuration between folders? Terragrunt solves this problem.
That’s exactly why I wrote this article. In the hope you find the same benefits in Terragrunt that I’ve found myself.
Give it a try, I’d love to hear about your experience!
Stay in touch
I hope you enjoyed this article as much as I enjoyed writing it!
Feel free to DM me any feedback on LinkedIn or email me directly. It will be highly appreciated.
That's what keeps me going!
Subscribe to get the latest articles from my blog delivered straight to your inbox!
About the author
Axel Mendoza
Senior MLOps Engineer
I'm a Senior MLOps Engineer with 6+ years of experience building production-ML systems.
I write long-form articles on MLOps to help you build too!