If you have spent any time managing Terraform configurations across more than a couple of environments, you have probably felt the pain that Terragrunt was built to solve.
You write a clean Terraform module for, say, an S3 bucket. Then you need that same bucket in development, staging, and production. Suddenly you are copying and pasting the same backend configuration block, the same provider block, the same variable definitions — across three directories, then six, then twelve as your infrastructure grows. Any change to that shared configuration means finding every copy and updating them one by one.
Terragrunt fixes that. It is a thin wrapper that sits on top of Terraform or OpenTofu and handles the repetitive scaffolding so your actual infrastructure definitions stay clean and consistent.
This guide explains what Terragrunt is, how it works in practice, when it makes sense to use it, and when it does not.
What Is Terragrunt?
Terragrunt is an open source tool built by Gruntwork that sits on top of Terraform or OpenTofu. It does not replace either tool — it calls them under the hood, just like a wrapper. What it adds is a layer of organisation and configuration management that Terraform and OpenTofu deliberately leave to the user to figure out.
The core idea is DRY: Don’t Repeat Yourself. In software development, DRY is a basic principle that says you should define something once and reference it everywhere it is needed, rather than copying it each time. Terraform is excellent at defining infrastructure as code, but it gives teams very little help with keeping that code DRY across environments, accounts, and regions. That is the gap Terragrunt fills.
💡 Think of it this way: Terraform defines what your infrastructure looks like. Terragrunt defines how you organise and run Terraform across many environments without copying the same configuration over and over.
Terragrunt reached version 1.0 in March 2026 — a significant milestone that signals its maturity and the Gruntwork team’s commitment to a stable, long-term API. It supports both Terraform and OpenTofu as its underlying execution engine, and current releases default to OpenTofu.
The Problem Terragrunt Solves
To understand why Terragrunt exists, it helps to look at the specific friction points that come up as Terraform configurations grow.
The Backend Configuration Problem
Every Terraform project needs a backend configuration that tells it where to store state. For a single environment, this is straightforward:
Now multiply this across twenty environments in three regions, each with a slightly different key path. That is sixty versions of nearly identical configuration blocks. Change the bucket name and you are making sixty edits.
Terragrunt lets you define the backend configuration once in a root terragrunt.hcl file and generate it automatically in every environment. The environment-specific part (the key path) is computed dynamically. The shared parts (bucket, region, locking table) are defined once.
The Module Repetition Problem
Terraform modules are the right way to share infrastructure patterns. But instantiating a module in each environment still requires a block of configuration per environment: the source path, the input variables, the provider, the backend. As the number of environments grows, so does the configuration overhead.
Terragrunt’s include block lets environments inherit shared configuration from a parent file, overriding only what differs between them. A production environment file might be ten lines long, with everything else inherited from the root configuration.
The Multi-Module Orchestration Problem
Real infrastructure is rarely a single module. A typical setup might have a networking module, a database module, an application module, and a monitoring module. These have dependencies: the database module needs the VPC from the networking module. The application module needs both the VPC and the database.
Terraform has no built-in way to express or enforce these dependencies across separate state files. Running modules in the wrong order causes failures. Running them all together in one state file creates a different set of problems.
Terragrunt’s dependency block lets you declare exactly these relationships, and its run-all command runs modules in the correct order automatically, respecting the dependency graph.
How Terragrunt Works
Terragrunt reads a terragrunt.hcl configuration file in each directory and uses it to wrap calls to Terraform or OpenTofu. When you run terragrunt plan, Terragrunt reads its configuration, generates any files that need to be generated (like the backend configuration), and then calls tofu plan or terraform plan with the right arguments.
The key building blocks are:
The root terragrunt.hcl File
At the top of your repository, a root terragrunt.hcl file defines the shared configuration that all environments inherit: the remote state backend, the provider configuration, any common variables. This is the single source of truth for everything that should be consistent across environments.
The path_relative_to_include() function is a Terragrunt built-in that automatically generates a unique state key for each environment based on its directory path. No more manual key management.
The include Block
Each environment’s terragrunt.hcl uses an include block to pull in the shared root configuration. The environment file then only needs to specify what is different: which module to use and what inputs to pass.
The backend configuration, provider configuration, and any other shared settings come from the root file automatically. The environment file only describes what makes production different from development.
The dependency Block
The dependency block lets you declare that one module depends on another and access its outputs. Terragrunt resolves these at plan and apply time, ensuring the database module runs before the application module, and that the application module can read the database’s endpoint as an input.
Terragrunt Stacks
Terragrunt Stacks, introduced as a stable feature in recent versions, provide a higher-level way to define groups of units and their dependencies. Rather than managing dependency relationships through individual dependency blocks scattered across directory trees, stacks let you declare the full dependency graph of a set of units in a single configuration file.
This is particularly useful for large organisations where a single deployment might span dozens of modules across multiple accounts and regions. Stacks make the dependency graph explicit and auditable, and they enable run-all to work more reliably at scale.
Terragrunt vs Terraform: What Each One Does
A question that comes up regularly is whether Terragrunt replaces Terraform. It does not. They work at different layers, and understanding those layers makes it much easier to decide whether Terragrunt is worth adding to your stack.
Terraform / OpenTofu does this
Terragrunt adds this on top
Defines infrastructure resources in HCL
Keeps those definitions DRY across environments
Manages a state file per configuration
Generates and manages remote state configuration automatically
Runs plan and apply against a single module
Orchestrates plan and apply across many modules in dependency order
Requires backend config in every module
Defines backend config once and distributes it everywhere
Has no built-in cross-module dependency system
Provides the dependency block for cross-module output references
Treats each directory as independent
Provides configuration inheritance via include blocks
No multi-module run command
run-all runs commands across an entire directory tree in the right order
The way to think about it: Terraform is the engine that talks to your cloud provider. Terragrunt is the organisational layer that makes running that engine across many environments manageable.
How to Install Terragrunt
Terragrunt is a single binary with no runtime dependencies beyond having Terraform or OpenTofu available on the same machine.
macOS
brew install terragrunt
Linux
Windows
Using tenv for Version Management
If your team works across projects that use different Terragrunt versions, tenv is worth considering. It manages Terraform, OpenTofu, Terragrunt, and Atmos versions from a single tool, similar to how nvm manages Node.js versions.
Pin your Terragrunt version explicitly in CI/CD to avoid version drift between team members. Subtle behaviour differences between Terragrunt versions are a common source of unexpected failures.
Dependency Management in Terragrunt
Managing dependencies between infrastructure modules is one of the areas where Terragrunt genuinely improves on what plain Terraform offers. The dependency block is the primary mechanism, and it does two things: it ensures modules run in the right order, and it makes outputs from one module available as inputs to another.
Declaring a Dependency
In the terragrunt.hcl file for a module that depends on another, you declare the dependency with a path to the other module’s directory. Terragrunt reads the other module’s state to retrieve its outputs.
Mock Outputs for Plan Runs
A practical challenge with dependency blocks is that you sometimes want to run a plan on a module before its dependencies have been applied and their outputs exist. Terragrunt’s mock_outputs feature lets you specify placeholder values that are used during plan but not during apply. This lets developers run plans locally without needing a fully provisioned environment.
run-all and Dependency Order
When you run terragrunt run-all plan or terragrunt run-all apply from a parent directory, Terragrunt builds a dependency graph from all the dependency blocks it finds and runs modules in the correct order. Modules with no dependencies run first. Modules that depend on others wait until those dependencies have completed.
⚠️ Heads up: run-all is powerful but worth treating with care in production. It runs applies in parallel where the dependency graph allows, and state lock contention between parallel runs is a real operational concern at scale. Consider running production applies one module at a time with explicit approval between them.
Terragrunt with OpenTofu
Terragrunt works with OpenTofu out of the box. Current Terragrunt releases default to using the tofu binary, and switching explicitly is a one-line change in your root terragrunt.hcl:
If you are migrating a Terragrunt setup from Terraform to OpenTofu, the process is the same as a standard Terraform-to-OpenTofu migration: install OpenTofu, update the terraform_binary setting, run terragrunt init in each module directory, and confirm the plans show no changes. The state file format is identical, so no state migration is needed.
Terragrunt with env zero
env zero supports Terragrunt natively, which means you can bring your existing Terragrunt configurations into env zero without rewriting them. Each Terragrunt unit — a directory with a terragrunt.hcl file — maps to an environment in env zero.
Where env zero adds value on top of what Terragrunt provides is in the governance layer. Terragrunt handles DRY configuration and module orchestration. env zero adds drift detection across your full Terragrunt stack, OPA policy enforcement before every apply, approval workflows, cost estimation, and a complete audit trail of every deployment — none of which Terragrunt manages on its own.
For teams that have outgrown managing CI/CD pipelines manually for Terragrunt workflows, env zero provides the automation and governance layer without requiring you to change your underlying terragrunt.hcl configurations.
🚀 You can connect your existing Terragrunt repository to env zero and run your first governed plan in minutes. Your terragrunt.hcl files stay exactly as they are.
When to Use Terragrunt — and When Not To
Terragrunt is a genuinely useful tool for the problems it was designed to solve. But it also adds a layer of complexity, and that layer is not always worth it. Being honest about when Terragrunt helps and when it does not will save your team from unnecessary overhead.
✅ Use Terragrunt when…
❌ Skip it when…
Managing infrastructure across multiple environments (dev, staging, prod)
You have one environment and a handful of modules
Running the same module in multiple AWS accounts or regions
Your entire infrastructure fits in a single Terraform state file comfortably
Need to share outputs between independent Terraform modules
Your team is new to Terraform and still learning the fundamentals
Backend configuration is getting copied across many directories
A managed platform already handles your orchestration and state
Need to orchestrate applies across many modules with dependency ordering
You want to keep your toolchain as simple as possible
Your team already uses Gruntwork’s module library
You are running a single small project with no multi-environment requirements
The rule of thumb used by experienced platform engineers: if you are not feeling the pain that Terragrunt solves, do not add it yet. Start with plain Terraform or OpenTofu. When you find yourself copying backend configuration for the third time, or when a missing dependency causes a failed apply, that is when Terragrunt starts to earn its place.
Summary
Terragrunt is a thin wrapper around Terraform and OpenTofu that solves a specific set of real problems: keeping backend and provider configuration DRY across many environments, managing dependencies between independent modules, and orchestrating multi-module deployments in the right order.
It does not replace Terraform or OpenTofu — it sits on top of them. Your existing HCL configurations work without modification. Terragrunt adds the organisational layer that Terraform deliberately leaves to the user.
If your infrastructure is small and contained, you probably do not need it yet. If you are managing multiple environments, accounts, or regions and finding yourself copying configuration repeatedly, Terragrunt is likely the right tool for that problem.
Terragrunt 1.0 reached stable release in March 2026, which is a good signal that the API has matured and the team is committed to stability. It is a well-supported, widely-adopted tool with a clear scope, and it does what it promises.
.webp)
