Article · part of a guide
Terragrunt Stacks: What terragrunt.stack.hcl Actually Does
Explicit Terragrunt stacks replace copy-pasted per-environment directories with a blueprint that generates units at runtime. What the unit and stack blocks do, what stack generate writes, and the two gotchas that cost people an afternoon.

Key takeaways
- Terragrunt has two kinds of stack. Implicit stacks come from how you lay out directories. Explicit stacks are declared in a terragrunt.stack.hcl file that generates unit directories at runtime.
- A unit block takes source, path, and values. Terragrunt writes each unit into .terragrunt-stack/<path>/ as a generated terragrunt.hcl plus a terragrunt.values.hcl holding that unit's values.
- A stack block has the same three arguments but points at another terragrunt.stack.hcl, which is how one environment pattern gets reused for dev and prod with different values.
- The .terragrunt-stack directory is generated output. Gitignore it and regenerate rather than committing it.
- terragrunt stack generate does not clean up units the current config no longer produces, so stale directories survive a rename until you run stack clean or regenerate with --source-update.
- A directory can be a unit or a stack, never both. Terragrunt tells them apart by whether it finds terragrunt.hcl or terragrunt.stack.hcl, so the two files can't sit together.
Terragrunt exists because people got tired of copying backend.tf into forty directories. Stacks exist because people then got tired of copying terragrunt.hcl into forty directories.
If you've run Terragrunt for a while, you know the shape of it. live/dev/us-east-1/vpc/terragrunt.hcl, then the same thing under staging, then under prod, each one a near-identical file whose only real content is a source, an include, and a handful of inputs. Adding a region means copying a tree. Changing the pattern means a find-and-replace across every copy, and missing one is how dev and prod drift apart.
Explicit stacks are Terragrunt's answer to that. You write the pattern once and let Terragrunt generate the directories.
What is a Terragrunt stack?
A stack is a collection of related units that Terragrunt manages together, so one command deploys the set and Terragrunt works out the dependency order between them. That much has always been true of a well-organized Terragrunt repo.
What's newer is that stacks now come in two flavors. An implicit stack is the one you already have: units arranged in a directory structure, no extra file involved. An explicit stack is declared in a terragrunt.stack.hcl file, which Terragrunt reads as a blueprint and uses to generate the unit configurations at runtime.
Terragrunt's own guidance is to start implicit and reach for explicit stacks when you've got reusable patterns, several environments that differ only in values, or a catalog other teams consume. That's sensible. If you have six units and one environment, a terragrunt.stack.hcl buys you nothing but indirection.
What goes in a terragrunt.stack.hcl file?
Two block types, both taking the same three arguments: source, path, and values.
A unit block defines one deployable piece of infrastructure:
unit "vpc" {
source = "git::[email protected]:acme/infrastructure-catalog.git//units/vpc?ref=v0.0.1"
path = "vpc"
}source says where the unit's configuration comes from, path is the directory name to generate it into, and values is a map of inputs handed to the unit. Each generated unit directory ends up with two files: a terragrunt.hcl and a terragrunt.values.hcl carrying that unit's values.
A stack block does the same thing one level up. Its source points at another terragrunt.stack.hcl, so what gets generated is itself a stack. Inside the referenced file, the values you passed are readable as values.<name>. That's the mechanism that lets a single environment pattern produce both dev and prod from one definition, which is the whole reason most teams look at explicit stacks in the first place.
One structural rule catches people early: a directory is either a unit or a stack, never both. A unit can't contain a terragrunt.stack.hcl, and a stack can't contain a terragrunt.hcl. Terragrunt uses exactly that to tell the two apart, so the files can't coexist in one directory.
What does terragrunt stack generate do?
It walks the current directory and everything beneath it, finds every terragrunt.stack.hcl, and writes a .terragrunt-stack directory with one subdirectory per unit path. Each of those holds the generated terragrunt.hcl. Generation runs in parallel, governed by GOMAXPROCS or --parallelism.
terragrunt stack generate
terragrunt stack run applyYou don't always have to run the first one. Commands that deploy units generate the stack automatically when it's missing. Running it explicitly is useful when you want to look at what you're about to apply before applying it, which on a first stack is time well spent.
Generation validates as it goes, checking that each unit's target directory contains a terragrunt.hcl and each nested stack's contains a terragrunt.stack.hcl. --no-stack-validate turns that off, though it's hard to think of a good reason to on a stack you're still building. Absolute paths get rejected, so everything stays relative to the working directory.
Where do stacks bite?
Two things, and the first one costs people an afternoon.
Generation doesn't clean up after itself. terragrunt stack generate won't remove files in .terragrunt-stack that your current configuration no longer produces. Rename a unit from vpc to network and you now have both directories sitting there, the old one holding a perfectly valid generated config pointing at infrastructure you thought you'd moved. The fix is terragrunt stack clean followed by terragrunt stack generate, or regenerating with --source-update. Make that reflex rather than something you remember after a confusing plan.
.terragrunt-stack is build output, so treat it that way. Gitignore it and regenerate. Committing generated directories recreates exactly the copy-paste sprawl the stack was supposed to remove, with the added pleasure of merge conflicts in files no human wrote.
How do Terragrunt stacks run on a managed platform?
This is where stacks get genuinely awkward for CI, and it's worth understanding why before you debug it.
With a conventional Terragrunt repo, your pipeline clones the repo and the unit directories are right there. With an explicit stack, they aren't. The repository holds a terragrunt.stack.hcl and some sources; the actual tree the tooling expects doesn't exist until something runs generation. Any pipeline step that assumes a unit directory is present (a lint pass, a formatting check, a plan) hits a tree that hasn't been built yet and fails in a way that reads like a broken config rather than a sequencing problem.
A platform that supports stacks properly has to generate the stack and discover its units before anything else runs. Scalr's runners do that generation and unit discovery ahead of the lint, plan, and apply steps, so those steps see a complete tree. Scalr requires Terragrunt 0.80.0 or later for native stacks, and versions below that now return a validation error rather than failing confusingly further along. Self-hosted agent pools need agent 1.6.0; Scalr-managed runners picked this up automatically. If you're rolling your own pipeline instead, the takeaway is the same: make generation an explicit first step, before anything that reads the tree.
Stacks are a different model from Terragrunt's run-all, which Scalr also supports and which comes with its own constraints around backends and unit state. They solve overlapping problems from different directions, and you'll usually pick one.
Should you move to explicit stacks?
Not reflexively. The test is whether you're maintaining copies of the same terragrunt.hcl that differ only in a few values, and whether the number of copies is growing. If it is, a stack converts that duplication into one definition plus a values map, and new environments stop being a copy operation.
If your tree is small, or your units genuinely differ from each other, you're trading a directory you can read for a blueprint you have to generate. That's a real cost. Terragrunt's own documentation recommending implicit stacks for smaller setups is good advice and not just a gentle on-ramp.
Start with one pattern you've already copied three times. Write it as a terragrunt.stack.hcl, run terragrunt stack generate, and read what comes out before you run anything against real infrastructure. If the generated tree matches what you'd have written by hand, you've found the right first candidate. For the groundwork underneath all of this, our beginner's guide to Terragrunt covers the include and dependency patterns stacks are built on top of.
Frequently asked questions
What is a Terragrunt stack?
A stack is a collection of related units that Terragrunt manages together, so you can deploy several components with one command and let Terragrunt resolve the dependency order. Terragrunt supports implicit stacks, which come from organizing units in a directory structure, and explicit stacks, which are declared in a terragrunt.stack.hcl file.
What is the difference between an implicit and an explicit Terragrunt stack?
An implicit stack is just a directory tree of units with no extra file. An explicit stack is declared in terragrunt.stack.hcl, which acts as a blueprint: it names the units to create, where each unit's configuration comes from, where to generate it, and what values to pass. Terragrunt's own documentation suggests starting with implicit stacks and adding explicit ones for reusable patterns as the estate grows.
What does terragrunt stack generate do?
It finds every terragrunt.stack.hcl at or below the current directory and writes a .terragrunt-stack directory containing a subdirectory per unit path, each holding a generated terragrunt.hcl. It validates that each unit source directory contains a terragrunt.hcl, which --no-stack-validate skips. Commands that deploy units generate the stack automatically if it is missing.
Should I commit the .terragrunt-stack directory?
No. It is generated output, and Terragrunt's documentation points out that keeping it out of version control and regenerating on demand is the straightforward approach. Add it to .gitignore.
Why is an old unit still showing up after I renamed it in terragrunt.stack.hcl?
terragrunt stack generate does not remove files in .terragrunt-stack that the current configuration no longer produces, so the directory from the old name survives alongside the new one. Run terragrunt stack clean followed by terragrunt stack generate, or regenerate with --source-update, to rebuild from a clean state.
Do Terragrunt stacks work on a managed platform?
They can, but the platform has to generate the stack before anything else touches the tree, because the unit directories do not exist in the repository. Scalr's runners generate the stack and discover its units before the lint, plan, and apply steps run. Scalr requires Terragrunt 0.80.0 or later for native stacks, and self-hosted agent pools need agent 1.6.0, while Scalr-managed runners get it automatically.
About the author

director of platform engineering at Scalr
Ryan Fee is the director of platform engineering at Scalr, with over 15 years of experience improving infrastructure experiences at companies large and small.
Part of this guide
3 sheets