RSS

Terragrunt vs. Terraform: The Full Comparison and Which to Choose

Terragrunt Terraform Cloud State Management Workspaces

Terragrunt vs. Terraform aren't really two competing tools. Instead, you're choosing how much orchestration you're willing to bolt onto a state model that was never built to reason across its own boundaries, a job Terragrunt does well, but from outside the engine.

TL;DR
$ cat terragrunt-vs-terraform.tldr
• What Terragrunt actually solves for Terraform users, and where it gets oversold or undersold.
• A side-by-side comparison table of Terraform alone versus Terraform with Terragrunt on top.
• Why Terraform workspaces and Terraform Cloud aren't substitutes for Terragrunt, even though people compare them.
• How to run Terraform, Terragrunt, and Stategraph together during a transition, and what it takes to retire Terragrunt for good.

Every team that scales past a handful of Terraform root modules eventually runs into the same wall.

At this point, they are likely to ask a familiar question: do we adopt Terragrunt to handle our multiple Terraform modules, or do we solve this ourselves? Terragrunt is a well-known solution, but it isn't the only one.

Terragrunt is a mature, widely adopted orchestration layer with real engineering behind it, built by people who hit the same wall and decided to fix it externally rather than wait for HashiCorp to fix it natively.

In this article, I explore how you can run Terraform and Terragrunt side by side, or even improve things with Stategraph, while keeping things running during the transition. I also cover why plenty of teams eventually retire Terragrunt.

What does Terragrunt solve?

The framing "Terragrunt vs. Terraform" is misleading. Terragrunt doesn't replace Terraform; it runs Terraform (or OpenTofu) underneath every command you type; it's a thin wrapper, not a second engine.

The real comparison is native Terraform against Terraform with a Terragrunt (or alternative) orchestration wrapper on top.

A handful of recurring pain points push teams toward that wrapper. Code duplication across multiple environments is one: a typical setup with a dev directory, a staging directory, a qa directory, and a production directory ends up with nearly identical main.tf, variables.tf, backend.tf, and outputs.tf files copied into each one, and any shared change (a new tag or a bumped provider version, for instance) has to be hand-applied everywhere it appears.

Backend configuration compounds the issue. Terraform's backend block famously can't take variables, since the backend has to be resolved before the rest of your configuration even loads.

As a result, each environment's bucket name, state key, and region has to be written out literally, or passed in through partial configuration (-backend-config files or flags) that you then have to wire into every run. Either way it's repetition that invites copy-paste mistakes (a production key pointed at a staging bucket is a classic, expensive one).

Then there's the lack of native dependency handling between separate root modules. Terraform builds a resource graph beautifully within a single root module, but the CLI has no concept that your vpc module and your app module, sitting in separate directories with separate state files, need to run in a specific order. Say your app module needs a subnet ID from the VPC module as an input:

# app/main.tf
variable "subnet_id" {
type = string
}
resource "aws_instance" "app" {
ami = "ami-0123456789abcdef0" # replace with an AMI ID from your region
instance_type = "t3.medium"
subnet_id = var.subnet_id
}

Nothing in plain Terraform enforces that the VPC module ran first, or that its output actually reaches this variable. You either wire it by hand with terraform_remote_state data sources and remember the order every time, or you write a script that does the remembering for you, which is really just manual orchestration wearing a CI badge. Teams turn to Terragrunt when they get tired of writing the script.

Terragrunt vs. Terraform and Terragrunt: a side-by-side comparison

Put next to each other, the catch looks like this. It's less duplication and manual orchestration on one side, and one more tool on the other.

Aspect Terraform alone Terraform + Terragrunt
Backend configuration Written out per environment (or passed via -backend-config), no variables allowed in the block Defined once in a root config, inherited via include
Code reuse across environments Copy-pasted directories or Terraform workspaces DRY terragrunt.hcl files pointing at shared modules
Dependency handling Manual, via terraform_remote_state and run order you track yourself Native dependency blocks resolve outputs and enforce order
Directory structure Whatever you choose, no imposed convention Opinionated hierarchy the whole team has to buy into
Multi-module orchestration Run each root module by hand, in the right order terragrunt run --all plan / run --all apply
Extra tooling surface None beyond Terraform itself A second CLI, a second config language dialect, a second release cadence to track

Terragrunt removes duplication and manual sequencing but, as well as a tool, it adds a directory convention and a learning curve on top of Terraform you already know.

The benefits and negatives of Terraform, used alone

For engineers on modern infrastructure teams, plain, vanilla Terraform's declarative configuration files are easy to read. Its module system, provider ecosystem, and plan/apply workflow remain the foundation of everything.

If you run Terraform by itself, you just have one tool, one CLI, full control over your own backend configuration, and zero dependency on a third party's release schedule.

The negatives show up as you scale to a multi-environment setup. Managing multiple environments with a folder-per-environment layout means the same provider.tf and backend block, duplicated three or four times, drifting slightly further apart each time someone edits one copy and forgets the others.

Terraform workspaces are a partial fix, letting you reuse one configuration against multiple state files, but they don't solve everything (more on that below).

Because a root module's dependency graph stops at its own boundary, if blast-radius causes you to split infrastructure into separate root modules, it comes at the expense of native ordering between them; you're back to scripts, or careful manual sequencing, every time a change touches more than one directory.

However, Terraform alone is still a good solution for a single environment or a small, stable footprint.

The benefits and negatives of Terragrunt, on top of Terraform

Terragrunt's include block lets child configurations inherit an entire root terragrunt.hcl, so environment-specific files shrink down to little more than the module source and a handful of input overrides.

Its dependency blocks give you native dependency management: declare that your app module depends on vpc, and Terragrunt resolves the VPC's outputs and enforces the run order automatically, no hand-rolled terraform_remote_state wiring required.

# app/terragrunt.hcl
include "root" {
path = find_in_parent_folders("root.hcl")
}
dependency "vpc" {
config_path = "../vpc"
}
terraform {
source = "../../modules/app"
}
inputs = {
subnet_id = dependency.vpc.outputs.subnet_id
}

The include block above points to a root.hcl file. Here is a minimal version, with the remote state defined once for every environment:

# root.hcl
remote_state {
backend = "s3"
generate = {
path = "backend.tf"
if_exists = "overwrite_terragrunt"
}
config = {
bucket = "my-company-terraform-state"
key = "${path_relative_to_include()}/terraform.tfstate"
region = "us-east-1"
encrypt = true
}
}

Remote state inheritance works the same way: define the remote_state block once in a root file, and every child configuration inherits a dynamically generated state key based on its own path, so no two environments can accidentally collide on the same key through a copy-paste error.

Multi-module orchestration follows from the same dependency graph; terragrunt run --all plan (older Terragrunt releases used run-all, which is now deprecated in favor of the run --all form) walks every unit in the current stack and executes them in the correct order.

Terragrunt's stacks feature gives teams a way to define reusable, versioned collections of infrastructure patterns through terragrunt.stack.hcl files, rather than relying purely on directory structure to imply a stack's shape.

However, there are additional negatives:

Terraform workspaces vs. Terragrunt

Terraform workspaces and Terragrunt aren't solving the same challenge, so they can't be compared as alternatives. Terraform workspaces let you reuse a single configuration against multiple, separate state files, which is useful if you want to spin up a short-lived copy of infrastructure to test a change before it touches production.

However, that is where Terraform workspaces hit their limits. CLI workspaces share one backend and one configuration across every instance; they change the state file's name, not the architecture underneath it. HCP Terraform workspaces are different: each has its own variables, permissions, and state, much closer to a separate root module.

If your environments need genuinely different resource sizing, different credentials, different access controls, or different compliance requirements, not just a different state key, workspaces won't provide that separation, and no amount of clever variable interpolation changes that.

Terragrunt, by contrast, gives each environment its own configuration and its own backend by design, while still keeping the shared parts DRY through inheritance. The real choice isn't workspaces versus Terragrunt; it's directory-per-environment, with or without Terragrunt orchestrating it, with workspaces reserved for the narrower, single-configuration use case.

Terragrunt vs. Terraform Cloud

Terragrunt and Terraform Cloud (now HCP Terraform) sit on different layers entirely, and treating them as substitutes misunderstands what each one actually does.

Terragrunt is a CLI wrapper you invoke locally or from CI; it orchestrates how Terraform commands run across your modules, but it doesn't host anything. Terraform Cloud is a hosted execution environment: a UI, remote runs, a private module registry, policy enforcement, and team collaboration features layered on top of the Terraform engine itself.

They aren't mutually exclusive. One workable option is to use HCP Terraform as the state backend for Terragrunt-managed modules, with the workspace's execution mode set to local. In local mode, remote runs, policy enforcement, and cost estimation don't apply.

HashiCorp's own approach to the orchestration gap, Terraform Stacks, addresses some of the same dependency-and-deployment-order constraints Terragrunt was built for, though it's part of HCP Terraform and Terraform Enterprise rather than an open-source CLI layer you can drop into any workflow. Availability varies by plan (custom deployment groups require Premium), so check HashiCorp's current pricing page.

When it comes to using Terragrunt or Terraform Cloud, you should ask yourself which layer, orchestration or execution, is actually causing you pain.

Managing Terraform, Terragrunt, and Stategraph together

Terragrunt gives teams DRY configuration and dependency ordering that Terraform's flat, single-state model doesn't provide natively. Stategraph builds on that in two separate steps: pull request workflows around what you already run, and, later, a different state engine.

Stategraph Orchestration runs plans and applies on pull requests, with policy checks and required approvals, against whatever you're already running, including Terraform, OpenTofu, Terragrunt, Pulumi, or CDKTF, on your own GitHub Actions or GitLab CI runners.

Your state stays exactly where it is. That means a team can adopt Stategraph's pull request workflow, drift detection, and governance on top of an existing Terragrunt setup today, without touching a single terragrunt.hcl file, and get PR-based plans, approvals, and scheduled drift detection wrapped around the orchestration they've already built.

Switching from Terragrunt to Stategraph

Stategraph Orchestration leaves your state where it is. The step that lets you retire Terragrunt is Stategraph's separate state engine, Infrastructure as a Database, which moves state into PostgreSQL. You can move root modules over one at a time, and Stategraph documents exporting a state back to plain .tfstate if you want to reverse course.

Stategraph's Infrastructure as a Database stores your state in PostgreSQL as a graph of resources and their dependencies, rather than as a flat JSON file per root module. Once a state is imported (stategraph import tf pulls in an existing Terraform state file and its HCL in one step), the jobs Terragrunt's dependency blocks and run --all handle become native engine behavior instead of an external wrapper's job.

A multi-state transaction can plan and apply several state directories as one atomic operation (stategraph tf mtx --out plan.json ./networking ./compute), with the dependency ordering resolved by the graph itself rather than by a directory structure and a dependency block you maintain by hand.

This migration is not a flip of a switch, but has its benefits. Structural changes, like renaming a resource or moving it into a module, get their own fix through stategraph refactor, which tracks HCL edits across a session and writes the Terraform moved blocks for you rather than requiring terraform state mv by hand.

The real benefit for teams that migrate is fewer tools to onboard new engineers into and one execution model instead of two layered ones.

Much of what Terragrunt compensates for, ordering across separate states, comes from a primitive: the single flat state file that can't reason across its own boundaries.

Fixing that primitive moves that part of the fix into the engine instead of a wrapper. It doesn't replace Terragrunt's other function: keeping configuration DRY across environments (shared backend blocks, generated files, common inputs), so plan for how you'll handle that once the wrapper is gone, for example with shared Terraform modules.

Which should you choose? Terraform, Terragraph, Stategraph, or a combination?

Stay with plain Terraform if you have a single environment or a small, stable footprint. Add Terragrunt when you're maintaining three or more near-identical environment folders and hand-sequencing changes across root modules. Consider Stategraph when ordering across separate states is the pain you'd rather have fixed in the engine than in a wrapper.

Conclusion

Terragrunt is a legitimate, well-engineered fix for real constraints: code duplication across environments, backend configuration that can't take variables, and a lack of native ordering between separate root modules.

Teams that adopt it are trading a learning curve and a directory convention for less manual orchestration, and for a lot of teams in a lot of environments, that's a fair trade.

But it's still an external answer to an internal problem. If you're already running Terraform and Terragrunt today, you don't have to choose between keeping what works and fixing the underlying model.

If you want to improve your state management and thin wrapper infrastructure-as-code setup, try Stategraph free and run it alongside your existing setup first, then decide, on your own timeline, how much of that orchestration you'd rather have handled natively.

Terragrunt vs. Terraform FAQs

Does Terragrunt replace Terraform?

No. Terragrunt calls Terraform or OpenTofu under the hood for every command it runs; you still write standard Terraform modules, and Terragrunt only adds a configuration layer that decides how and in what order those modules get invoked. Nothing about your actual .tf resource definitions changes because Terragrunt is in the picture.

Do you need Terragrunt from day one on a new project?

Not usually. A single environment or an early-stage project with a handful of modules doesn't have the duplication or dependency-ordering issues Terragrunt exists to solve, so adding it before you feel that pain just adds a second tool and an opinionated directory structure for no immediate benefit.

Most teams add it once they notice they're maintaining three or more near-identical environment folders by hand.

Can you use Terragrunt and Terraform Cloud together?

Yes, with a caveat. Terragrunt can use HCP Terraform as its state backend, with the workspace's execution mode set to local. Remote runs and the policy and cost features that depend on them only work in remote execution mode.