Most release notes are a list of things you can now opt into. Terragrunt 1.1 is mostly a list of things you have already opted into, whether or not you noticed.
Six features that used to sit behind --experiment are now the default behaviour: stack-dependencies, cas, catalog-redesign, mark-many-as-read, opt-out-auth and dag-queue-display. A feature you have to enable is a feature you evaluated. A default is something that changes under your CI while nobody is looking at it.
Two questions, then. What did they build that's actually good, and what moved underneath your pipeline?▸The 60-second version — flip through the deck8 slides · swipe →
Stack dependencies: the part worth an afternoon
A Terragrunt stack generates a tree of units from a single terragrunt.stack.hcl. The awkward part has always been the wiring. Unit app needs the VPC ID from unit vpc, so you define a dependency block in the catalog unit and then feed it a path from the stack file through values.
That works, and it puts your directory layout inside your shared catalog. Reorganize the tree and you patch the catalog for everyone.
autoinclude moves the relationship to where the stack is defined:
# terragrunt.stack.hcl
unit "vpc" {
source = "github.com/acme/catalog//units/vpc"
path = "vpc"
}
unit "app" {
source = "github.com/acme/catalog//units/app"
path = "app"
autoinclude {
dependency "vpc" {
config_path = unit.vpc.path
}
inputs = {
vpc_id = dependency.vpc.outputs.vpc_id
}
}
}
Terragrunt writes a terragrunt.autoinclude.hcl next to the generated terragrunt.hcl and merges it into the unit. The unit.<name>.path reference resolves to the generated path, so the layout stops being a shared secret between the catalog and the stack.
The catalog stopped needing to know where you keep your directories.
Patch instead of fork
The second-order effect matters more than the syntax. Anything valid in a unit configuration is valid in an autoinclude block, which means you can add configuration a catalog unit never shipped with:
unit "app" {
source = "github.com/acme/catalog//units/app"
path = "app"
autoinclude {
errors {
retry "transient_errors" {
retryable_errors = [".*Error: transient network issue.*"]
max_attempts = 3
sleep_interval_sec = 5
}
}
}
}
Every platform team knows the alternative: one environment needs a retry rule, the shared unit doesn't have one, so someone forks the unit. Six months later there are three forks and nobody remembers which is canonical.
Two smaller additions round it out. include blocks now work in terragrunt.stack.hcl, so shared stack configuration can live in a parent folder. And dependency blocks can target stack directories, with the run queue expanding them to the units inside. That relationship is one-way: units can depend on stacks, stacks cannot depend on stacks or units.
CAS stopped being a git thing
The content addressable store deduplicates source downloads by content instead of by URL. It used to be a git-only experiment. Now it covers everything Terragrunt fetches.
git:: sources. This is what the experiment already did.beforetfr:// deduplicate too.newIdentical files occupy disk once regardless of how many configurations use them. Two attributes give you control, both off by default: update_source_with_cas rewrites a relative source into a content-addressed cas:: reference during stack generate, so a generated tree stops depending on the surrounding repository layout. mutable = true on a terraform block copies content instead of hard-linking it, which costs I/O and disk but leaves the working tree editable.
If you need the old behaviour for a run, --no-cas (or TG_NO_CAS=true) turns it off.
What moved under your pipeline
This is the part I'd read twice before upgrading a shared repo.
Reading detection is the basis of change-based runs in CI. Previously, pointing a unit's terraform block at a local directory did not mark the files inside that directory as read, so editing the module did not select the unit. That was a real gap, and closing it is correct. It also means a --filter 'reading=./modules/vpc/main.tf' job now selects units it skipped before:
terragrunt run --all --filter 'reading=./modules/vpc/main.tf' -- plan
For files the detector doesn't track on its own, the new mark_glob_as_read() function expands a glob and marks every match as read in one call.
Skip 1.1.1
One honest warning about the patch line. 1.1.1 fixed chained role assumption for the S3 backend, and in doing so broke setups that supply static AWS credentials and configure a role through iam_role, --iam-assume-role or TG_IAM_ASSUME_ROLE. Terragrunt assumes the role once at the start of a run, and every later call uses that session. In 1.1.1 backend operations started performing a second assumption, so the role tried to assume itself and AWS answered AccessDenied. 1.1.2 fixes it, and also cuts find_in_parent_folders() lookups: in the project's own micro-benchmark, resolving the root configuration for 100 units nested eight directories deep went from 4.8 ms to 0.49 ms.
Where I've landed
In June I said 1.0 was the release that made the floor stop moving, and that Runner Pool plus --filter were the reasons you would actually re-platform onto Terragrunt now. 1.1 doesn't change that verdict. It does something quieter: it takes the pile of --experiment flags that early adopters were carrying in their CI config and folds them into the product.
The upside is real. autoinclude is the first time stack wiring reads like a description of the system instead of a description of the filesystem.
The thing to keep in your head is that a graduated experiment behaves differently from a new feature. A new feature waits for you. A default is already running.
Which of the six were you already running with --experiment? And has anyone measured what reading detection did to the size of their PR-scoped plan? I'd genuinely like the before and after numbers.