github.com/gruntwork-io/terragrunt
v1.1.3
#422 most downloaded on Go modules
gruntwork-io/terragrunt
What this package is like to depend on
Last release 2 days ago
21 Aug 2026
Ships on a steady schedule
a new release about every 8 days
Rarely documented
notes for 7 of 856 stable releases
Nothing withdrawn
no release was ever pulled
10 years old
1565 releases · first in 2016
397 releases in the last 12 months
see the full history below
Release timeline
1565 releases · Oct 2021 to Aug 2026Releases
latest 60 of 1565-
v1.1.4-0.20260821191953-286a33cb8f7921 Aug 2026 pre-releaseNothing published for this version
-
v1.1.4-0.20260820171355-27f01f0d9b3420 Aug 2026 pre-releaseNothing published for this version
-
v1.1.4-0.20260819193651-7377d20eae4019 Aug 2026 pre-releaseNothing published for this version
-
v1.1.4-0.20260819152407-699f617dabcc19 Aug 2026 pre-releaseNothing published for this version
-
v1.1.4-0.20260819111402-7345aba35d5319 Aug 2026 pre-releaseNothing published for this version
-
v1.1.4-0.20260819000324-5a375f53a75819 Aug 2026 pre-releaseNothing published for this version
-
v1.1.4-0.20260818203101-9a7914cc261e18 Aug 2026 pre-releaseNothing published for this version
-
v1.1.4-0.20260818135623-cde6f1d495fb18 Aug 2026 pre-releaseNothing published for this version
-
v1.1.4-0.20260817192613-9473a2fe85cf17 Aug 2026 pre-releaseNothing published for this version
-
v1.1.4-0.20260816100452-6856c549d9d316 Aug 2026 pre-releaseNothing published for this version
-
v1.1.4-0.20260814151744-4e93185e4d2814 Aug 2026 pre-releaseNothing published for this version
-
v1.1.4-0.20260813161524-7822f15daaf513 Aug 2026 pre-releaseNothing published for this version
-
v1.1.4-0.20260813153301-bb6efb6488d013 Aug 2026 pre-releaseNothing published for this version
-
v1.1.313 Aug 2026Release notes
Open source →🐛 Bug Fixes
Fixed
Unsupported attributeerrors forvalues.*inputs thatautoincludeoverridesA unit input referencing a
values.*key that the unit's values file doesn't define no longer fails withUnsupported attributewhen anautoincludeblock supplies that input. The autoinclude value is applied as intended.# stacks/terragrunt.stack.hcl unit "subnet" { source = "../units/subnet" path = "subnet" autoinclude { dependency "vpc" { config_path = unit.vpc.path mock_outputs = { vpc_id = "mock" } } inputs = { vpc_id = dependency.vpc.outputs.vpc_id } } values = { cidr_block = "10.0.0.0/24" } }
# units/subnet/terragrunt.hcl inputs = { vpc_id = values.vpc_id # supplied by autoinclude, not the values file cidr_block = values.cidr_block # still resolves from values file }
Fixed
overwrite_terragruntandremove_terragrunton files with no trailing newlinegenerateblocks usingif_exists = "overwrite_terragrunt"orif_disabled = "remove_terragrunt"failed to properly handle existing files when the file at the target path had no newline after its first line, empty files included.Terragrunt now properly handles files like this, so a file carrying the Terragrunt signature is overwritten or removed as configured, and a file without it produces the usual error naming the path Terragrunt would not touch.
Dependency
mock_outputsapply when the state bucket doesn't exist yetWhen reading a dependency's outputs directly from remote state (
--dependency-fetch-output-from-state), Terragrunt fell back tomock_outputsonly when the state object was missing, not when the S3 bucket itself didn't exist. Adependencyon an environment that hadn't been bootstrapped yet would fail instead of using its mocks.A missing bucket is now treated the same as a missing state object, so commands like
planandvalidatecan resolve mocks before the dependency's backend has been created.Source permissions preserved on hidden directories copied by
include_in_copyWith the
fast-copystrict control enabled, a hidden directory that Terragrunt copied due toinclude_in_copymatching something within it took the permissions of the first file generated within it, instead of the permissions it had in the source.Those directories now keep their source permissions, matching the copy Terragrunt performs with the control disabled.
Applied the positive half of a filter that begins with a negation
When a
--filterquery began with a negation, Terragrunt treated the whole query as an exclusion. The expressions chained after the negation stopped restricting the selection and only narrowed what got subtracted, so components matching none of them came back in the results. Those expressions are now applied.$ terragrunt list bar baz foo
$ terragrunt list --filter '!name=foo | name=bar' bar baz foo$ terragrunt list --filter '!name=foo | name=bar' barThis follows the left-to-right refinement that
|has everywhere else: each expression narrows what the one before it selected. A query is only treated as an exclusion when every one of its expressions is negated, such as'!name=foo'or'!name=foo | !name=bar'.See Combining Expressions for how negation, intersection and union interact.
Fixed a race condition that left cached provider archives in the working directory
With the provider cache server enabled via
--provider-cache, a race let the server start responding to requests before it had finished preparing the directories it caches into. A provider requested in that window had its archive and lock file written relative to the working directory instead of into the cache, leaving zip files behind in your project.That race condition has been fixed. Providers now always download into the cache directory.
Fixed a race condition between concurrent Terragrunt runs downloading providers
A race condition in the logic used to synchronize provider downloads meant that two Terragrunt runs on the same machine could interfere with each other while caching the same provider. Each run staged its downloads at the same path, so a run that finished first could delete an archive another run was still unpacking, failing that run with
failed to open zip archive.That race condition is now fixed. Two runs can cache the same provider at the same time.
Fixed space-delimited flag values in
providers lockThe space-delimited form,
providers lock -platform linux_amd64, now reaches OpenTofu and Terraform intact. Previously it was the attached form,-platform=linux_amd64, that worked: given the value as a separate argument, Terragrunt moved it to the end of the command, where it was read as a provider address and the run failed withInvalid provider type "linux_amd64".-fs-mirrorand-net-mirrorwere moved the same way, and now keep their values too.With
--provider-cacheenabled, platforms are also split correctly across the per-platformproviders lockruns used to warm the cache.Fixed
scaffoldon units and stacksterragrunt scaffoldread every source as an OpenTofu/Terraform module. Given a unit or a stack, which are Terragrunt configurations rather than OpenTofu/Terraform modules, it exited successfully having written an invalidterragrunt.hclfile.Units and stacks are now scaffolded the way the Catalog TUI scaffolds them: their files are copied into the working directory for you to edit in place, along with a
terragrunt.values.hcllisting everyvalues.*reference the configuration makes.terragrunt scaffold 'github.com/gruntwork-io/terragrunt-scale-catalog//units/aws/oidc/iam-oidc-role'Copying refuses to overwrite: a file that would land on an existing path stops the command before anything is written. Modules and templates are unaffected and are still scaffolded from their variables.
See Scaffold for what gets copied and how the values file is filled in.
Answered every prompt when input is piped in
A run that asks for confirmation more than once, such as
terragrunt backend deleteprompting for both the lock table entry and the state object, used to read only the first answer when the answers were piped in rather than typed. The remaining answers were discarded while reading ahead, and the next prompt failed with an end-of-input error. Every prompt in a run now reads from the same input, so pipingyesfor each one works.Stack dependencies honor
mock_outputswith--dependency-fetch-output-from-stateA
dependencyblock that reads outputs from a stack (itsconfig_pathpoints at aterragrunt.stack.hcldirectory) used to fail when a unit in that stack had no state yet, even when the dependency declaredmock_outputs. This blocked commands likeplanandvalidateagainst a stack that hadn't been applied.Such a dependency now falls back to
mock_outputsfor the units that have no state yet. In a partially applied stack, applied units resolve to their real outputs while the rest use their mocks.Mocks for a stack dependency are keyed by unit name, so
mock_outputshas to be a map or object. Declaring it as any other type now reports that directly, instead of leaving the units it can't cover out of the stack outputs.Fixed
--config=being ignored by thetflinthookThe built-in
tflinthook reads the configuration file out of the arguments you give it, then uses that path fortflint initand for the lint run. It only recognized the space-separated--config <path>spelling, so a hook written as:before_hook "tflint" { commands = ["plan"] execute = ["tflint", "--config=custom.tflint.hcl"] }
was treated as though no configuration file had been named at all. Terragrunt searched the unit directory and its parents for a
.tflint.hclfile instead, and either failed with a config-not-found error or rantflint initagainst whatever unrelated configuration the search turned up. Terragrunt now recognizes--config <path>,--config=<path>,-c <path>, and-c=<path>.The hook also builds
--vararguments from the unit'sinputsand fromTF_VAR_entries inextra_argumentsblocks. Those arguments came out in a different order on every run, which made the logged command line, and anything comparing it between runs, needlessly unstable. They are now ordered by variable name.🧪 Experiments Added
block-iterationexperiment reserves theexpansionblockThe
block-iterationexperiment has been added as the gate for iterating adependency,unit, orstackblock over acountorfor_each, declared through a nestedexpansionblock, along with anenabledattribute onunitandstackblocks.In this release the flag is reserved only, and enabling it has no behavioral effect. Writing an
expansionblock without the experiment now reports an error naming the flag, rather than leaving the block to be silently discarded:the unit "app" block in /path/to/terragrunt.stack.hcl uses an expansion block, which requires the 'block-iteration' experiment; enable it with --experiment block-iterationTrack progress and share feedback in #4504.
bounded-discovery— Added a directory boundary for graph traversalFilter expressions that traverse the dependency graph reach beyond the working directory: dependents (
--filter '...{unit}') by walking up to the Git repository root, dependencies (--filter '{unit}...') by following declared paths. Either way, Terragrunt reads and parses every configuration it touches. In monorepos with isolated environments, that traversal can fail or do wasted work reading sibling environments.Enable the new
bounded-discoveryexperiment to set a boundary for that traversal. The--discovery-boundaryflag (env:TG_DISCOVERY_BOUNDARY) replaces the Git repository root as the enclosure for a whole run:cd environments/staging terragrunt run --all plan --experiment bounded-discovery --filter '...{vpc}' --discovery-boundary .
The experiment also unlocks an inline
(dir)boundary operand, which bounds a single expression and overrides the flag. It occupies the same slot as a traversal depth, so it bounds discovery by location the way a number bounds it by graph hops:cd environments/staging terragrunt run --all plan --experiment bounded-discovery --filter '(.)...{vpc}'
Any configuration that resolves outside the boundary, whether a dependent or a dependency, is not read, parsed, or returned:
finddoes not list it andrun --alldoes not run it. Configurations inside the boundary are discovered as usual.The boundary must be an existing directory, and relative paths are resolved against the working directory. Dependent traversal searches upward from the working directory, so filters that use it also need the boundary to be the working directory or one of its parents. Dependency traversal follows declared paths from the units a filter matched, so dependency-only filters accept any directory, including one below the working directory:
# From the repository root, follow app's dependencies but keep them within prod terragrunt find --experiment bounded-discovery --filter '{./prod/app}...' --discovery-boundary ./prod
Reserving
(and)for the boundary operand changes how--filterreads those characters everywhere, not only when the experiment is enabled. An expression such as--filter '1...(foo | bar)'previously matched a unit literally named(fooorbar); it is now rejected as a malformed boundary. Wrap a name or path containing parentheses in braces (e.g.--filter '{./weird(name)}') to keep it literal.browse-tui— Added an interactive browser for your estateThe new
browse-tuiexperiment adds theterragrunt browsecommand. With the experiment enabled,terragrunt browseopens a three-column Terminal User Interface (TUI) browser of your infrastructure estate: the parent directory on the left, the current directory in the middle, and a detail pane on the right showing metadata for the highlighted unit, stack, or directory. The browser opens immediately and fills in metadata as discovery completes in the background.Enable it with
--experiment browse-tuiorTG_EXPERIMENT=browse-tui. See the experiment documentation for the keybindings, search, and the criteria for stabilization.mutable-generate— Deduplicatedgenerateblock outputThe
mutable-generateexperiment has been added. With it enabled, the contents agenerateblock produces are stored in the Content Addressable Store (CAS), and the file written atpathis a read-only link to that stored copy rather than a file of its own.Since the stored copy is addressed by the hash of its contents, anything generating identical contents links to the same copy. A
generateblock inherited by several hundred units therefore costs one copy in.terragrunt-cacherather than several hundred.The link is read-only because that copy is shared. Where a generated file does need to be edited in place, a new
mutableattribute on thegenerateblock gives it a writable file of its own:generate "provider" { path = "provider.tf" if_exists = "overwrite" mutable = true contents = "..." }
Setting
mutablewithout the experiment enabled is an error, since earlier Terragrunt versions reject the attribute. The CAS is required, so--no-caswrites generated files directly andmutablehas no effect.For details, see the experiment documentation.
optional-dependency-outputs— Added--no-dependency-outputsflag to skip dependency output resolutionAdded a
--no-dependency-outputsflag that skips all dependency output resolution globally, mirroring the existingskip_outputs = trueattribute on individualdependencyblocks.The feature is gated behind the
optional-dependency-outputsexperiment:TG_EXPERIMENT=optional-dependency-outputs terragrunt run --no-dependency-outputs -- init
Using
--no-dependency-outputswithout enabling theoptional-dependency-outputsexperiment will return an error.Thanks to @pjrm for contributing this feature!
🧪 Experiments Updated
catalog-format— Added reading the catalog as JSON LinesThe
catalogcommand draws a terminal user interface, and refuses to start where there is no terminal to draw it on. With thecatalog-formatexperiment enabled,--format=jsonlwrites the same discovery to standard output instead, as one JSON object per line:terragrunt catalog --experiment=catalog-format --format=jsonl | jq -c '{kind, title, component_source}'
Entries are written as they are discovered rather than collected first, so output is readable while the remaining repositories are still loading, and a reader that stops early ends the command quietly:
terragrunt catalog --experiment=catalog-format --format=jsonl | head -5Note
Closing the pipe
In this example, the
headprogram exits after reading in five lines, and Terragrunt detects theSIGPIPEsignal from the OS, and shuts down cleanly.Entries appear in discovery order, which interleaves the repositories being loaded and differs between runs. Every entry carries the complete body of the component's README in the
docfield. Combine usage of Terragrunt with other tools likejqto drop it.terragrunt catalog --experiment=catalog-format --format=jsonl | jq -c 'del(.doc)'
Entries follow a published JSON schema. For the fields and their meanings, see Non-interactive catalog.
--format=tuiis the default, and leaves the terminal user interface exactly as it was.catalog-format— Added reading the catalog as MarkdownThe
catalog-formatexperiment gains a second non-interactive format. Where--format=jsonlwrites a record per catalog entry for a program to parse,--format=mdwrites one Markdown document for a person or an agent to read:terragrunt catalog --experiment=catalog-format --format=md > catalog.mdEach entry becomes a section holding the metadata the catalog user interface shows for it, the source the component is scaffolded from, and the component's README. Sections are written as entries are discovered, so the document is readable while the remaining repositories are still loading.
READMEs are reproduced inside fenced blocks, so the headings one carries are not read as sections of the catalog document. The document closes with a table naming every component it holds and a count of what was discovered, which is how a reader tells a complete document from one that was cut short by a consumer that stopped reading.
For the fields each section carries, see Non-interactive catalog.
oci— Added OCI sources for stack units and stacksterragrunt.stack.hclnow acceptsoci://sources inunitandstackblocks, so a stack can pull its components straight from an OCI registry. Without theociexperiment enabled, such a source fails with a clear error instead of an unsupported-scheme failure.oci— Added OpenTofu CLI-config credentials for OCI module sourcesoci://module downloads now read OpenTofu's CLI-config credentials, so one configuration serves both OpenTofu and Terragrunt.Terragrunt honors the
oci_credentials "<registry>[/<repo-prefix>]"blocks (username and password, OAuth tokens, or adocker_credentials_helper, which liketofumay only be set on a whole registry) and theoci_default_credentialsfallback helper. ATF_CLI_CONFIG_FILEorTERRAFORM_CONFIGvalue selects the config file outright; otherwise Terragrunt reads the first of~/.tofurcand~/.terraformrcthat exists, and merges the*.tfrcand*.tfrc.jsonfiles in OpenTofu's config directory.Terragrunt picks the most specific matching source across CLI config and ambient Docker config; an explicit CLI-config entry wins when both match equally. Set
discover_ambient_credentials = falsein theoci_default_credentialsblock to use CLI config only.⚙️ Process Updates
Go bumped to
v1.26.5The version of Golang used to compile the Terragrunt binary has been updated from
v1.26.0tov1.26.5.Thanks to @apoiget for contributing this upgrade!
Pull Requests
✨ Features
- feat: Adding graph boundary via
()syntax by @yhakbar in #6365 - feat: Adding
--discovery-boundaryflag by @yhakbar in #6355 - feat(getter): OpenTofu CLI-config credentials for oci:// sources by @denis256 in #6531
- feat: Adding
browseby @yhakbar in #6219 - feat: Adding
mutableattribute to thegenerateblock by @yhakbar in #6563 - feat: Adding
mdformat forcatalogby @yhakbar in #6608 - feat: Add --skip-dependency-outputs flag to skip dependency output resolution by @pjrm in #6422
🐛 Bug Fixes
- fix(providercache): log -lockfile=readonly skip at debug level by @bryanhorstmann in #6577
- fix: Fixing handling of
EOFingenerateblocks by @yhakbar in #6592 - fix: Fixing the
--config=form of flags used in the tflint hook by @yhakbar in #6591 - fix: Fixing fast-copy ancestor directory permissions by @yhakbar in #6593
- fix: Addressing
providers lock-platformusage with space delimited values by @yhakbar in #6597 - fix: Fixing bug with
negation | positiveexpression in the same query. by @yhakbar in #6598 - fix: Fixing provider cache server archive dir race by @yhakbar in #6620
- fix: Fixing scaffold on units and stacks by @yhakbar in #6607
- fix: Addressing feedback from #6565 and #6605 by @yhakbar in #6628
- fix: autoinclude values override for inputs by @denis256 in #6626
- fix: Fixing
mdformat catalog escaping by @yhakbar in #6638 - fix: Plumbing through
evalCtxfor discovery boundary by @yhakbar in #6632 - fix: Fixing stack dependency mock outputs by @yhakbar in #6530
- fix: Fixing issue where dependency mock outputs aren't used when bootstrapping hasn't run yet. by @yhakbar in #6534
🏎️ Performance
📖 Documentation
- docs: Add call out for terragrunt scale in quick start by @yhakbar in #6583
- docs: document oci module sources, authentication, and caching by @denis256 in #6636
- docs: Cleaning up changelog for
v1.1.3by @yhakbar in #6669 - docs: Cleaning up experiment docs by @yhakbar in #6627
- docs: address review feedback on the oci and autoinclude docs by @denis256 in #6643
✅ Tests
- test(getter): integration tests against a local OCI distribution registry by @denis256 in #6614
- test: prove oci module portability between tofu and terragrunt by @denis256 in #6629
- test(git): add unit coverage for internal/git command wrappers and parsers by @denis256 in #6661
🧹 Chores
- chore: Pin exact provider versions for terralith to terragrunt guide by @yhakbar in #6578
- chore: Using vfs handle for
ParseFromFileby @yhakbar in #6561 - chore: Walk in discovery with vfs by @yhakbar in #6564
- chore: Registring
catalog-formatexperiment by @yhakbar in #6582 - chore(deps): update AWS, Azure, GCP SDKs by @denis256 in #6590
- chore: Continuing clean-up of
go test ./...on a fresh clone of the repo by @yhakbar in #6553 - chore: Fixing usage of deprecated aws sdk by @yhakbar in #6600
- chore: Cleaning up profile tests per feedback in #6553 by @yhakbar in #6599
- chore: Clean-up by @yhakbar in #6584
- chore: Addressing feedback from #6365 and #6355 by @yhakbar in #6603
- chore: Register the
block-iterationexperiment by @yhakbar in #6562 - chore: address review feedback from #6531 by @denis256 in #6609
- chore: Addressing flake in
TestCatalogJSONLFormatCleansUpOnEarlyExitby @yhakbar in #6613 - chore: updated TestDiscovery_GraphConcurrentConfigAccessWithRacing to use VFS by @denis256 in #6622
- chore: Adding expansion detection and internal expansion logic by @yhakbar in #6565
- chore: Adding expansion blocks to the configs that accept expansion by @yhakbar in #6605
- chore: Threading venv through getters and
hcl fmtby @yhakbar in #6621 - chore: Addressing feedback from #6621 by @yhakbar in #6633
- chore: Fixing experiment tag in sidebar by @yhakbar in #6635
- chore: Updating mem exec so that it fails closed by @yhakbar in #6634
- chore: Gate real
hgusage test behind theexecbuild flag by @yhakbar in #6637 - chore: Replacing aws provider with null provider in
init-cachefixture by @yhakbar in #6639 - chore: Cleaning up
NewParsingContextconstructor by passing in venv as a param by @yhakbar in #6630 - chore: Addressing lint finding by @yhakbar in #6649
- chore: Refactoring markdown deps into
internal/mdby @yhakbar in #6640 - chore: Adding unit tests for internal packages by @denis256 in #6660
- chore: Bumping Go to
1.26.5(#6664) by @apoiget in #6666 - chore: Dropping stale tree parse test case by @yhakbar in #6672
- feat: Adding graph boundary via
-
v1.1.3-0.20260807190014-8e77bfbd328407 Aug 2026 pre-releaseNothing published for this version
-
v1.1.3-0.20260807090600-17dd0e15b19007 Aug 2026 pre-releaseNothing published for this version
-
v1.1.3-0.20260806194821-a9e38fb590f706 Aug 2026 pre-releaseNothing published for this version
-
v1.1.3-0.20260806095114-8e178f8f0f5306 Aug 2026 pre-releaseNothing published for this version
-
v1.1.3-0.20260804201903-c82c4d48912b04 Aug 2026 pre-releaseNothing published for this version
-
v1.1.3-0.20260803142150-15b4b0adca3b03 Aug 2026 pre-releaseNothing published for this version
-
v1.1.3-0.20260731212024-8905b2398b5731 Jul 2026 pre-releaseNothing published for this version
-
v1.1.3-0.20260730173015-7de08a356cb430 Jul 2026 pre-releaseNothing published for this version
-
v1.1.229 Jul 2026Release notes
Open source →✨ New Features
Scaffold straight from the catalog README view with
ctrl+dIn the
terragrunt catalogTUI, pressingctrl+dwhile reading a component's README now scaffolds it immediately, skipping the interactive form. Module and template inputs are written as# TODOplaceholders, and unit/stack copies get a fully placeholderterragrunt.values.hcl. The hint bar at the bottom of the README view advertises the new key.🏎️ Performance Improvements
Fewer filesystem checks when resolving
find_in_parent_folders()find_in_parent_folders()walks up from a unit toward the filesystem root, checking each directory for the configuration file it was asked to find. Even when the call named a file, as infind_in_parent_folders("root.hcl"), each directory along the way was also checked for the default configuration filenames. Units sharing a parent chain then repeated every check their siblings had already made.Terragrunt now checks only the filename the call names, and reuses what it already learned about a directory for the rest of the command. Deeply nested estates benefit most, since every level between a unit and its root configuration used to be re-checked once per unit.
In micro-benchmarks, resolving the root configuration for 100 units nested eight directories deep went from 4.8ms to 0.49ms. Across the benchmarked shapes the lookups run between 7x and 10x faster, and the time saved grows with both the number of units and how deeply they sit below their root configuration.
🐛 Bug Fixes
Fixed roles assuming themselves for backend operations
A regression in v1.1.1 broke setups that provide static AWS credentials and configure a role via the
iam_roleattribute, the--iam-assume-roleflag, orTG_IAM_ASSUME_ROLE.In those setups, Terragrunt assumes the role once at the start of a run, and every later AWS call uses that role session. In v1.1.1, backend operations like bootstrapping the state bucket started performing an extra role assumption of their own. Since the run was already using the role session at that point, the role tried to assume itself, and AWS rejected the call with an
AccessDeniederror unless the role's trust policy happened to include the role itself.Backend operations now reuse the role session from the start of the run, as they did before v1.1.1.
This does not affect the
assume_roleattribute of theremote_stateblock. Roles configured there are backend-specific and are still assumed on top of the supplied credentials, so the cross-account role assumption should continue to work as expected.Local sources no longer re-init when uncopied files change
For units with a local
source, Terragrunt decides whether the cached copy is stale by hashing the source directory. That hash previously covered every file in the directory, including hidden files andexclude_from_copymatches that are never copied into the cache. Creating or touching such a file (an editor swap file, a scratch note) changed the hash, forcing a needless re-copy and auto-init on the next run.The hash now covers only the files a copy would deliver, honoring the default hidden-file rule along with
include_in_copyandexclude_from_copy. Files that never reach the cache no longer trigger re-initialization.Fixed
widthtruncation of colored and multi-byte log contentThe
widthoption in a custom log format sizes a column to a fixed number of visible characters. When the content held color codes or multi-byte characters and was longer than the column, truncation cut the raw bytes: it could slice through the middle of a color code, leaving color bleeding into the rest of the line, or split a multi-byte character into invalid output, and it dropped more visible text than the configured width.widthnow measures and cuts by visible characters. Color codes are preserved intact, multi-byte characters are never split, and the column keeps exactly the requested number of visible characters.Provider cache downloads now require a secret URL
The Provider Cache Server now hardens the download endpoint that fetches provider archives on the caller's behalf. That endpoint attaches whatever registry credentials are configured for the upstream host, and it was the only one on the server that did not require the token generated for the run, so any other process on the machine could use a running cache server to pull artifacts from a private registry with the credentials of whoever started the run.
The download URLs handed to OpenTofu and Terraform now carry a secret path segment, generated fresh each time the cache server starts and redacted from the server's own logs. Requests that omit the segment get a 404.
Run report no longer mangles the names of paths that share a prefix with the working directory
When a run's path shared a string prefix with the working directory without being nested under it, the run report shortened its name by shearing off the prefix mid-segment. A working directory of
/repo/projectalongside a run at/repo/project-staging/unitproduced the name-staging/unit.The report now shortens a path only when it is genuinely nested under the working directory. Sibling paths keep their full name.
Feature flag defaults no longer leak between units in
run --allA
featureblock'sdefaultwas recorded once per run and shared by every unit. Duringrun --all, the first unit to be parsed set the value for a flag name, so a unit definingdefault = falsecould evaluatefeature.toggle.valueastruebecause a sibling unit was parsed first. Which unit won depended on parsing order, making the result vary between runs.Defaults are now resolved per unit, including defaults inherited through
include. Overrides passed with--featureorTG_FEATUREcontinue to apply to every unit in the run.Thanks to @dhotcolorado for reporting and fixing this!
Fixed S3 source downloads under EKS Pod Identity
Downloading unit sources from private S3 buckets (
s3::https://...) now works when EKS Pod Identity is the only credential source. Previously, the bundledaws-sdk-gov1 rejected the Pod Identity Agent endpoint (169.254.170.23) because it only allowed loopback hosts. Terragrunt now usesaws-sdk-gov1.55.6, which allows the EKS and ECS container credential endpoints.🧪 Experiments Added
otel-logsexperiment exports logs to OpenTelemetryTerragrunt previously emitted only traces and metrics, so there was no way to ship its log output to an OpenTelemetry backend or correlate log lines with the spans of a failed run.
Enable the new
otel-logsexperiment to add an OpenTelemetry logs signal, configured withTG_TELEMETRY_LOGS_EXPORTER:none- no log exporting, the default.console- write log records to the console as JSON.otlpHttp- export logs to an OpenTelemetry collector over HTTP.otlpGrpc- export logs to an OpenTelemetry collector over gRPC.
TG_TELEMETRY_LOGS_EXPORTER=otlpHttp terragrunt run --all --experiment otel-logs -- apply
The OTLP exporters read the endpoint from the standard
OTEL_EXPORTER_OTLP_ENDPOINTenvironment variable. SetTG_TELEMETRY_LOGS_EXPORTER_INSECURE_ENDPOINT=trueto disable TLS when collecting locally. Records emitted while a span is active carry its trace and span IDs, so a failed unit's logs link to its span in the backend. Without the experiment enabled, the logs exporter stays inert regardless ofTG_TELEMETRY_LOGS_EXPORTER.profilingexperiment adds pprof collection for Terragrunt runsEnable the new
profilingexperiment to collect CPU profiles, memory (heap) profiles, and goroutine profiles (stack traces of all goroutines) using CLI flags. Profiling is intended for debugging the performance of Terragrunt itself, and for exploring ways to optimize Terragrunt as an application; it will not help with improving the performance of the infrastructure Terragrunt manages.Example:
terragrunt --experiment=profiling --profile-cpu cpu.prof --profile-mem mem.prof --profile-goroutine goroutine.prof run -- plan
Use
--profile-dirto collect all profiles into a single directory with conventional names (terragrunt_cpu.prof,terragrunt_mem.prof,terragrunt_goroutine.prof):terragrunt --experiment=profiling --profile-dir /tmp/profiles run --all -- plan
The same behavior is available via environment variables when the
profilingexperiment is enabled:TG_PROFILE_CPUTG_PROFILE_MEMTG_PROFILE_GOROUTINETG_PROFILE_DIR
When using
--profile-dirorTG_PROFILE_DIR, Terragrunt also setsTOFU_CPU_PROFILEfor each unit so downstream OpenTofu processes (OpenTofu 1.11 or later) write their own CPU profiles into unit-specific subdirectories. An explicitly setTOFU_CPU_PROFILEis never overridden.🧪 Experiments Updated
azure-backendnow manages Azure Storage remote stateThe
azure-backendexperiment now enables functional Terragrunt support for the Azure Storage (azurerm) remote-state backend.When the experiment is enabled, Terragrunt can bootstrap the resource group, storage account, and blob container used by
remote_state { backend = "azurerm" }, detect whether the backend needs bootstrapping, converge blob versioning and soft-delete settings, delete state blobs or containers, and migrate state blobs within the same storage account.Terragrunt-only settings such as
location, the storage account SKU options, theskip_*flags,enable_soft_delete,soft_delete_retention_days, andmsi_resource_idare consumed by Terragrunt and removed before it runs OpenTofu/Terraform withinit -backend-config, so the underlyingazurermbackend receives only keys it understands.msi_resource_idis not bootstrap-only: it also selects the managed identity used for delete and migrate.This remains opt-in while the experiment is active:
terragrunt --experiment azure-backend run -- plan
Thanks to @omattsson for driving this support forward.
oci- Credential helpers for OCI module sourcesoci://module downloads now use the Docker credential helpers you already have configured, so registries like Amazon ECR authenticate automatically with no extra setup.oci- Content-addressable caching for OCI module sourcesoci://module sources now integrate with Content Addressable Storage. When theociexperiment is enabled, downloads are cached by their manifest digest, so a repeated fetch of the same tag or digest is served from the local store instead of re-downloaded from the registry.Mutable tags stay correct: every fetch re-resolves the tag to its current manifest digest at download time, so re-pushing a module under the same tag invalidates the cache and pulls the new content rather than serving a stale copy. A digest-pinned source (
?digest=sha256:...) skips registry resolution and keys the cache directly.oci- Downloading modules from OCI registriesThe
ociexperiment now downloads source code (including OpenTofu modules) from OCI Distribution registries. When enabled, Terragrunt acceptsoci://source URLs in Terragrunt configurations (includingterraform.sourceattributes). Specify eithertagordigest; omitting both selects thelatesttag.//subdirselectors are supported. Artifacts follow the same publishing contract OpenTofu 1.10 consumes natively.Authentication covers static credentials via interim
TG_TMP_OCI_*environment variables and read-only ambient discovery of Docker and containers auth files. Static credentials can be limited to one registry withTG_TMP_OCI_REGISTRY; without it, the configured token or username and password may be offered to any registry the process contacts. Credential helpers (such asecr-login) are not invoked yet, so registries that need per-run token minting only work while an externally obtained login is present in an ambient file.When the experiment is disabled,
oci://sources remain unsupported.For setup steps, see the experiment documentation.
Pull Requests
✨ Features
- feat(getter): implement OCIGetter.Get with fake-store unit tests by @denis256 in #6479
- feat: Add
otel-logsexperiment by @yhakbar in #6279 - feat(getter): add static and ambient OCI credential discovery by @denis256 in #6483
- feat(getter): add WithOCI and gate oci sources behind the oci experiment by @denis256 in #6486
- feat(getter): add OCI digest CAS resolver with tag re-resolution by @denis256 in #6503
- feat: Adding earlier catalog bail by @yhakbar in #6493
- feat(profiling): add automatic pprof collection by @denis256 in #5711
- feat(getter): credential helpers for oci:// module sources by @denis256 in #6508
- feat: add experimental azurerm remote state backend by @denis256 in #6428
🐛 Bug Fixes
- fix: Fixing docs
TF_TOKEN_*rendering by @yhakbar in #6509 - fix: Preventing spurious re-inits by @yhakbar in #6504
- fix: Fixing log truncation by @yhakbar in #6526
- fix: support EKS Pod Identity for S3 source downloads by @denis256 in #6532
- fix: Isolate feature defaults per unit in run --all by @dhotcolorado in #5995
- fix: Adding random URL segment to download URI by @yhakbar in #6547
- fix: Fixing report path prefix trim by @yhakbar in #6527
- fix: Fixing self-chained role assumption by @yhakbar in #6521
- fix: prevent auto-init env vars from leaking into main command by @yapret in #6576
🏎️ Performance
📖 Documentation
- docs: Adding CLI flag precedence rule by @yhakbar in #6524
- docs: Adding changelog entry for #5995 by @yhakbar in #6548
- docs: Re-organizing content related to the run queue out of stack documentation by @yhakbar in #6114
- docs: Adding search telemetry by @yhakbar in #6555
- docs: Improving docs by addressing frequently asked questions by @yhakbar in #6560
🧹 Chores
- chore: Log Windows console mode retrieval failures at debug level (#6374) by @AgustinSabalza in #6376
- chore: Fixing code fences on
/reference/hcl/blocks/by @yhakbar in #6485 - chore: Avoid package-level module resolution for
versionattribute by @yhakbar in #6482 - chore: AWS dependencies bump by @denis256 in #6502
- chore: Running
fd -tf -e go -x golines -wto avoid run-on lines by @yhakbar in #6484 - chore: Adding some integration testing for the
versionattribute by @yhakbar in #6487 - chore: Unify Venv struct by dropping
cas.Venvby @yhakbar in #6488 - chore: Adding
vsopsby @yhakbar in #6506 - chore: speed up slowest tests with unit-level coverage and hermetic fixtures by @denis256 in #6436
- chore: lint fixes by @denis256 in #6518
- chore(deps): bump astro from 7.0.4 to 7.1.0 in /docs by @dependabot[bot] in #6515
- chore: Fixing panic in Windows test by @yhakbar in #6536
- chore: Update grpc, x/mod, go-shellwords deps by @denis256 in #6543
- chore: Adding more tests for build metadata by @yhakbar in #6538
- chore: Adding vhttp client to abstract away HTTP client connections by @yhakbar in #6121
- chore: Cleaning up tests for #6547 by @yhakbar in #6549
- chore: Refactor for network isolation in tests by @yhakbar in #6507
- chore: fixed failed lint tests by @denis256 in #6550
- chore: Fixing pprof venv access by @yhakbar in #6556
- chore: Updating Kapa integration by @yhakbar in #6558
- chore: coverage report fixes by @denis256 in #6557
- chore: Reducing race in vexec testing by @yhakbar in #6551
-
v1.1.2-0.20260728063237-40a9e719e01828 Jul 2026 pre-releaseNothing published for this version
-
v1.1.2-0.20260727182513-a580abb9240827 Jul 2026 pre-releaseNothing published for this version
-
v1.1.2-0.20260727163904-e7fc8c376e0527 Jul 2026 pre-releaseNothing published for this version
-
v1.1.2-0.20260727160702-ff1f96db8ed227 Jul 2026 pre-releaseNothing published for this version
-
v1.1.2-0.20260726173707-f23dfe05e4ab26 Jul 2026 pre-releaseNothing published for this version
-
v1.1.2-0.20260724152627-33b9fcef137524 Jul 2026 pre-releaseNothing published for this version
-
v1.1.2-0.20260723184830-70f849a8750d23 Jul 2026 pre-releaseNothing published for this version
-
v1.1.2-0.20260722142304-aefcd8b9d6ae22 Jul 2026 pre-releaseNothing published for this version
-
v1.1.2-0.20260721162559-ec76ac47676121 Jul 2026 pre-releaseNothing published for this version
-
v1.1.2-0.20260720205812-5c9fce1afdc120 Jul 2026 pre-releaseNothing published for this version
-
v1.1.2-0.20260720165802-1cc733e3a94f20 Jul 2026 pre-releaseNothing published for this version
-
v1.1.2-0.20260717180134-533bf59afb3717 Jul 2026 pre-releaseNothing published for this version
-
v1.1.2-0.20260717140918-a6beaee9354317 Jul 2026 pre-releaseNothing published for this version
-
v1.1.2-0.20260716135559-d45d4a5261df16 Jul 2026 pre-releaseNothing published for this version
-
v1.1.2-0.20260714172302-37c443c263fd14 Jul 2026 pre-releaseNothing published for this version
-
v1.1.2-0.20260714143103-0b936d70e3ca14 Jul 2026 pre-releaseNothing published for this version
-
v1.1.114 Jul 2026Release notes
Open source →🐛 Bug Fixes
Chained role assumption for the S3 backend
When AWS credentials were supplied through
--auth-provider-cmdor environment variables, Terragrunt ignored theassume_roleattribute of theremote_stateblock for its own backend operations, such as bootstrapping the state bucket. In cross-account setups this caused access errors, even though OpenTofu/Terraform itself assumed the role correctly during runs.Terragrunt now uses the supplied credentials as the source identity and assumes the configured role on top of them. The same applies to roles configured via the
iam_roleattribute or the--iam-assume-roleflag, and to fetching dependency outputs directly from S3 state.Safer temporary clone directories for
terragrunt catalogTerragrunt now creates a fresh temporary clone directory for each catalog load, rejects symlinked clone roots, and removes catalog clones when the TUI session exits.
Resolve
dependencyoutputs for units that reference a dependency in a hook,extra_arguments, orremote_stateblockResolving a unit's
dependencyoutputs for a downstream unit no longer fails when that unit references its own dependency in:- a
before_hook,after_hook, orerror_hook - an
extra_argumentsblock - a
remote_stateblock
Previously these raised
There is no variable named "dependency"on the downstream unit, and aremote_statereference could crash Terragrunt.Limit IaC engine archive extraction
Terragrunt now protects developer machines and CI runners from engine archives that expand into unexpectedly large amounts of data. If an IaC engine package is unusually large or contains too many files, Terragrunt stops processing it before it can consume excessive disk space.
--filter-allow-destroywith...[]dependent-traversal filters no longer fails--filter-allow-destroy --filter '...[HEAD~1...HEAD]'failed with "Too many command line arguments" or hung when the deleted unit had dependents. Terragrunt now correctly plans and destroys deleted units regardless of whether dependents are included in the run.Fix
findandlistmissing units inside generated stacks for Git-based filtersterragrunt findandterragrunt listwith a Git-based filter (for example--filter '[HEAD^1...HEAD]') now detect units inside generated stacks. Previously they did not generate stacks in the worktrees they create for the comparison, so no unit nested in a generated stack was ever surfaced, whileterragrunt run --allwith the same filter targeted those units correctly.This affected every change that lands inside a generated stack, including a modified
terragrunt.stack.hcl, a change to a unit's own files, and a change to a file the stack reads viaread_terragrunt_configormark_glob_as_read.Stacks are generated only inside the comparison worktrees;
findandliststill do not generate stacks in your current working directory by default.Treat Git source
refvalues strictly as referencesTerragrunt now passes the
reffrom a Git module source to git strictly as a reference when downloading through content-addressable storage. Previously a source whoserefbegan with a git option (for example a value starting with--) could be interpreted by git as an option rather than a reference while fetching the source.Terragrunt now terminates git option parsing before the repository and reference arguments in its
fetch,clone, andls-remoteinvocations, so these values can only ever be read as the repository and reference they are meant to be. Normal refs, branches, tags, and commit SHAs continue to work unchanged.hcl validateresolvesget_original_terragrunt_dir()to the discovered unitterragrunt hcl validateandterragrunt hcl validate --inputsnow resolveget_original_terragrunt_dir()to each discovered unit's own directory instead of the directory the command was launched from. Previously, when the command ran from a parent directory that discovered units in subdirectories, anyread_terragrunt_config()call that built a path relative toget_original_terragrunt_dir()resolved against the wrong directory and failed with "You attempted to run terragrunt in a folder that does not contain a terragrunt.hcl file", even thoughplan,apply, andrun validateworked on the same configuration.Both commands now set the original config path per discovered unit before parsing, matching the behavior of
runandbackend bootstrap, so relative paths resolve against the unit that owns them.Respect
-lockfile=readonlyduring provider cachingWhen you pass
-lockfile=readonlytoinit, Terragrunt no longer generates or updates.terraform.lock.hclwhile warming the provider cache. Previously the cache step could write the lock file before OpenTofu/Terraform ran, so the read-only check always passed and silently defeated the flag.Terragrunt now leaves the lock file untouched and lets OpenTofu/Terraform enforce it, failing when the lock file is missing or incomplete. The flag is honored whether it is supplied on the command line or through the
TF_CLI_ARGSorTF_CLI_ARGS_initenvironment variables.run --allno longer crashes on dependency discovery with graph filtersRunning
run --allwith a filter that expands a git range through the dependency graph (for example[HEAD~1...HEAD]...) could fail during dependency discovery, reporting that a component "is missing its working directory". Whether it happened depended on the size and shape of the changed unit's dependency closure, so the same filter succeeded on smaller branches andfindwas unaffected.A dependency reached from several units at once could become visible to discovery before its working directory was set, so a concurrent traversal could read it before it was complete. Dependencies now have their working directory set before they become visible, so
run --allbehaves the same regardless of graph size.terraform_binaryrespected byrun --allwhen bothtofuandterraformare onPATHrun --allignored a unit'sterraform_binarysetting and fell back to the auto-detected default (OpenTofu when both binaries are onPATH). The per-unit options used to execute each unit are cloned from the stack options, whose binary path is the auto-detected default, and the configured value was never applied to them.Each unit now honors its own
terraform_binary, matching the behavior of a singlerun. Setting--tf-pathorTG_TF_PATHstill takes precedence over the config value.S3 bucket creation failures report the underlying error
When creating the state bucket failed during backend bootstrap, the reported error was a misleading
NoSuchBucketfrom a follow-up access check, hiding the actual cause. The original creation error, such asAccessDenied, is now part of the reported message.Allow empty
localsblocks interragrunt.stack.hclFixed a bug where an empty
locals {}block in a stack configuration could breakstack generate.Clear error when
terraform.sourcereferences a dependency outputA
terraform.sourcethat referencesdependency.<name>.outputs.<key>is now rejected with a message explaining that the module source must be resolvable before dependencies are evaluated.Terragrunt resolves the source while discovering units and building the run queue, before any dependency has run, so such a source can never be satisfied. Previously it surfaced a cryptic decode error.
🧪 Experiments Added
oci- Module sources from OCI registriesThe
ociexperiment has been added as the gate for downloading source code (including OpenTofu modules) from OCI Distribution registries usingoci://schema URLs in Terragrunt configurations (includingterraform.sourceattributes). This targets the same registries OpenTofu 1.10 supports natively, such as Amazon ECR, GitHub Container Registry, Azure Container Registry, Google Artifact Registry, and self-hosted or air-gapped registries.Enabling the experiment has no behavioral effect yet: the getter that will resolve
oci://sources is not wired into source downloading, sooci://sources still fail to download. Functional support will land in follow-up releases, gated by this experiment.For setup steps, see the experiment documentation.
version-attribute- Resolve registry modules from a version constraintThe
version-attributeexperiment has been added to gate a newversionattribute on theterraformblock. It holds a version constraint (such as~> 3.3or>= 1.0.0, < 2.0.0) for atfr://registry module, and Terragrunt resolves it to the highest published version that satisfies the constraint before downloading:terraform { source = "tfr://registry.opentofu.org/terraform-aws-modules/vpc/aws" version = "~> 3.3" }
This brings the
terraformblock to parity with theversionargument on OpenTofu and Terraformmoduleblocks. The attribute applies totfr://sources only, and cannot be combined with an inline?version=on the same source.Enable it with
--experiment version-attribute. For setup steps and the criteria for stabilization, see the experiment documentation.⚙️ Process Updates
Friendly panic reports
Terragrunt now writes a
terragrunt-crash-YYYYMMDDTHHMMSSZ-<pid>.logfile when it crashes.The report includes runtime details, the command line, the panic message, and the stack trace. You can conveniently share this file (after reviewing for sensitive information) to report panics if Terragrunt crashes.
Pull Requests
✨ Features
- feat: terragrunt panic reporting by @denis256 in #6120
- feat(experiment): introduce oci experiment flag for OCI module sources by @denis256 in #6461
- feat(getter): add OCIGetter by @denis256 in #6478
- feat: Add and validate the
versionattribute on theterraformblock by @yhakbar in #6475 - feat: Gate and resolve the version constraint at download time by @yhakbar in #6477
🐛 Bug Fixes
- fix: Avoiding generation of the lockfile when users supply
-lockfile=readonlyby @yhakbar in #6358 - fix: Fixing combination of
--filter-allow-destroywith graph + Git expression combo by @yhakbar in #6322 - fix: use updated/correct GTM tag by @ZachGoldberg in #6439
- fix(engine): limit engine ZIP archive extraction by @denis256 in #6437
- fix: Generate stacks in worktrees generated for
find/listby @yhakbar in #6362 - fix(catalog): harden temporary clone paths by @denis256 in #6438
- fix(git): pass repository and ref as positionals in git fetch, clone, and ls-remote by @denis256 in #6452
- fix(hcl-validate): set per-unit OriginalTerragruntConfigPath so get_original_terragrunt_dir() resolves correctly by @denis256 in #6445
- fix: Dependency output resolution for more scenarios by @yhakbar in #6425
- fix: Prevent
terraform_binaryfrom being ignored inrun --allby @yhakbar in #6460 - fix: Fixing chained role assumption for backend by @yhakbar in #6327
- fix: Fixing empty locals block for
stack generateby @yhakbar in #6470 - fix: Fixing
run --allwith graph expression throwing on missing working dir by @yhakbar in #6474
📖 Documentation
- docs: Fixing docs builds by @yhakbar in #6434
- docs: Documenting the
version-attributeexperiment by @yhakbar in #6476 - docs: Changelog fix-up by @yhakbar in #6481
🧹 Chores
- chore(deps): bump actions/cache from 5.0.5 to 6.1.0 by @dependabot[bot] in #6430
- chore(deps): bump mikepenz/action-junit-report from 6.4.1 to 6.4.2 by @dependabot[bot] in #6431
- chore(deps): bump the js-dependencies group across 1 directory with 4 updates by @dependabot[bot] in #6432
- chore: Addressing weekly reports (2026-06-29) by @yhakbar in #6435
- chore: Move env to venv by @yhakbar in #6406
- chore: Cleaning up #6406 by @yhakbar in #6462
- chore(deps): bump golang.org/x/crypto in /test/flake by @dependabot[bot] in #6464
- chore: Register the
versionattribute experiment by @yhakbar in #6463 - chore(deps): bump the js-dependencies group across 1 directory with 6 updates by @dependabot[bot] in #6456
- chore: Cleaning up leaking buckets by @yhakbar in #6466
- chore(deps): bump the go-dependencies group across 1 directory with 15 updates by @dependabot[bot] in #6457
- chore(deps): bump docker/setup-docker-action from 5.2.0 to 5.3.0 by @dependabot[bot] in #6454
- chore: add separated us-west-2 pass by @denis256 in #6473
- chore: OCI container fix by @denis256 in #6465
- chore: Resolve registry module versions from a constraint by @yhakbar in #6471
- chore: Moving writers to venv by @yhakbar in #6410
- chore: Removing
sourcefromversionattribute error by @yhakbar in #6480
📝 Other Changes
- a
-
v1.1.1-0.20260713142607-7c5056bf5fbf13 Jul 2026 pre-releaseNothing published for this version
-
v1.1.1-0.20260710191907-03868dc4ec9710 Jul 2026 pre-releaseNothing published for this version
-
v1.1.1-0.20260708164144-b5e4be00883e08 Jul 2026 pre-releaseNothing published for this version
-
v1.1.1-0.20260707161340-5d6ab6e56f7007 Jul 2026 pre-releaseNothing published for this version
-
v1.1.1-0.20260706140456-d2921580f47306 Jul 2026 pre-releaseNothing published for this version
-
v1.1.1-0.20260702064720-6bc00883791002 Jul 2026 pre-releaseNothing published for this version
-
v1.1.1-0.20260701221738-f9d48bfa5cc001 Jul 2026 pre-releaseNothing published for this version
-
v1.1.1-0.20260701194011-54e417ad969d01 Jul 2026 pre-releaseNothing published for this version
-
v1.1.1-0.20260701162154-47b1df95ac3e01 Jul 2026 pre-releaseNothing published for this version
-
v1.1.1-0.20260701122747-093c01cd754001 Jul 2026 pre-releaseNothing published for this version
-
v1.1.1-0.20260701110646-acec0a2afc8001 Jul 2026 pre-releaseNothing published for this version
-
v1.1.030 Jun 2026Release notes
Open source →✨ New Features
Stack dependencies
A stack generates a tree of units from a single
terragrunt.stack.hclfile. Wiring one of those units to another used to mean definingdependencyblocks in your catalog and threading dependency paths throughvalues. Stack dependencies let you declare those relationships up front instead.Add an
autoincludeblock inside aunitorstackblock, and Terragrunt generates a partial configuration (aterragrunt.autoinclude.hclfile) next to the generatedterragrunt.hclorterragrunt.stack.hclthat's automatically merged into the unit or stack definition. The newunit.<name>.pathandstack.<name>.pathreferences resolve to generated paths, so you don't have to hardcode them:# 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 } } }
Anything that's valid in a unit configuration is valid in its
autoincludeblock, so you can also patch catalog units with configuration they don't ship with, like retry rules:# terragrunt.stack.hcl 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 } } } }
The same works for nested stacks: an
autoincludeblock inside astackblock patches the generatedterragrunt.stack.hcl, so you can, for example, add an extra unit to one environment without forking the stack in your catalog.Stack configurations also gained two capabilities along the way:
includeblocks now work interragrunt.stack.hclfiles, so shared stack configuration can live in a parent folder.dependencyblocks can target stack directories, and the run queue expands them to the units inside. Note that this relationship only goes one way: units can depend on stacks, but stacks cannot depend on stacks or units.
See the stacks documentation for the full reference. Previously gated behind the
stack-dependenciesexperiment, all of this is now enabled by default.Content Addressable Store (CAS)
The Content Addressable Store (CAS) deduplicates source downloads across configurations. It addresses repositories and modules by their content, stores them locally, and serves later requests from that local store instead of repeating the fetch. This speeds up catalog cloning, OpenTofu/Terraform source fetching, and stack generation, and identical files occupy disk space once regardless of how many configurations use them.
The CAS is no longer limited to Git. It also deduplicates HTTP, Amazon S3, Google Cloud Storage, Mercurial, and SMB sources, along with OpenTofu/Terraform registry sources fetched via
tfr://. See supported sources for how each one resolves and deduplicates content.CAS is enabled by default. Use the
--no-casflag (orTG_NO_CAS=true) to opt out of it for a run:terragrunt run --all --no-cas -- plan
Two new attributes give you finer control, and both default to off:
-
update_source_with_casmakes a generated stack self-contained. Set it on aunit,stack, orterraformblock with a relativesource, andterragrunt stack generaterewrites that source into a content-addressedcas::reference, so the generated tree no longer depends on the surrounding repository layout. Catalog authors can keep relative paths in their sources and still ship a portable, reproducible stack:# stacks/networking/terragrunt.stack.hcl unit "vpc" { source = "../..//units/vpc" path = "vpc" update_source_with_cas = true }
After
terragrunt stack generate, the relative path is replaced by a reference to the exact tree the CAS stored:# Generated output unit "vpc" { source = "cas::sha1:f39ea0ebf891c9954c89d07b73b487ff938ef08b" path = "vpc" update_source_with_cas = true }
-
mutablecontrols how the CAS places fetched content on disk. By default, the CAS hard links files from its shared store into.terragrunt-cacheand marks them read-only, which is fast and uses no extra space, but means the files can't be edited in place. Setmutable = trueon aterraformblock to copy the content instead, making the working tree safe to edit at the cost of extra I/O and disk space:# units/vpc/terragrunt.hcl terraform { source = "github.com/acme/catalog//modules/vpc" mutable = true }
Previously gated behind the
casexperiment, the CAS no longer requires--experiment cas.Redesigned
terragrunt catalogThe
catalogcommand has been redesigned. It now starts without any configuration, discovers components across your catalog repositories in the background, and streams them into the TUI as they're found.Discovery is no longer limited to a
modules/directory; components can live anywhere in a catalog repository. To control what gets discovered, add a.terragrunt-catalog-ignorefile with.gitignore-style globs for the paths you want filtered out.Components in the TUI now carry metadata to help you navigate a large catalog: each one shows a kind label (
template,stack,unit, ormodule) and optional tags defined in the front-matter of itsREADME.md. From the component list, presssto open a new screen that interactively collects the values used to scaffold the component into your repository.Previously gated behind the
catalog-redesignexperiment, the redesigned catalog is now the defaultterragrunt catalogexperience.Reading detection for local module sources
Terragrunt can select units by the files they read, which is the basis of change-based runs in CI. Previously, pointing a unit's
terraformblock at a local directory didn't mark the files inside that directory as read, so a change to the module wouldn't select the unit.When a unit's source is a local module, Terragrunt now records the module's
*.tf,*.tf.json,*.hcl,*.tofu, and*.tofu.jsonfiles as read by that unit, so--filter 'reading=<path>'and--queue-include-units-readingselect the unit when a module file changes:terragrunt run --all --filter 'reading=./modules/vpc/main.tf' -- planFor files that reading detection doesn't track on its own, the new
mark_glob_as_read()HCL function expands a glob and marks every matching file as read in one call:locals { configs = mark_glob_as_read("${get_terragrunt_dir()}/config/{*.yaml,**/*.yaml}") }
Existing pipelines built on
--queue-include-units-readingorreading=filters may select more units than before, because changes to local module files now count as reads. Previously gated behind themark-many-as-readexperiment, these behaviors no longer require--experiment mark-many-as-read.Skip auth during discovery with
--no-discovery-auth-provider-cmdBy default, Terragrunt runs your
--auth-provider-cmdonce for every unit it discovers, so HCL functions that need credentials resolve correctly during parsing. In a large repository, that can mean hundreds of invocations before any unit runs, which can dominate wall-clock time on change-based runs.The
--no-discovery-auth-provider-cmdflag (env:TG_NO_DISCOVERY_AUTH_PROVIDER_CMD) skips those invocations during the discovery phase, leaving auth to run only for the units that actually execute:terragrunt run --all \ --no-discovery-auth-provider-cmd \ --queue-include-units-reading=./changed-file.txt \ -- plan
Warning
Use this only when you know parsing resolves without credentials. Units whose configuration depends on values from
--auth-provider-cmdduring discovery (for example, viaget_aws_account_id()) will fail to parse when the flag is set.Previously gated behind the
opt-out-authexperiment, the flag now works without--experiment opt-out-auth.Run queue displayed as a dependency tree
Before a
run --all, Terragrunt lists the units it's about to run. That list now renders as a dependency tree by default instead of a flat list, with units nested under their dependencies, so the run order and the relationships between units are visible before anything executes:The following units will be run, starting with dependencies and then their dependents: . ├── monitoring ╰── vpc ╰── database ╰── backend-appThe header adapts to direction: dependencies come before dependents on apply, and the order reverses on destroy.
Previously gated behind the
dag-queue-displayexperiment, the tree display no longer requires--experiment dag-queue-display.💡 Tips Added
Tip when filtering a stack leaves nested stacks ungenerated
terragrunt stack generate --filter './my-stack | type=stack'generates only the selected
stack, not the nested stacks it contains, which can be surprising for a stack of stacks.
When a non-glob| type=stackfilter leaves a stack's nested stacks ungenerated, Terragrunt
now prints a tip showing how to generate them too, for example
--filter './my-stack | type=stack' --filter './my-stack/** | type=stack'.🐛 Bug Fixes
Fix
permission deniedwhen generated files overwrite CAS-materialized filesWith the CAS enabled, Terragrunt fetches sources as read-only files. Writing a generated file over one of them no longer fails with
permission denied:- Files from
generateblocks withif_exists = "overwrite", when the module ships the target file (for example, its ownversions.tf). terragrunt.values.hcl, when the unit or stack source already contains one.terragrunt.autoinclude.hcl, when the unit or stack source already contains one..terraform.lock.hcl, when the provider cache server updates a committed lock file duringinit -upgrade.
In each case, the read-only file is replaced with a writable one, and the shared CAS store is never modified.
Fix
permission deniedwhen CAS fetches a git source across filesystemsWith the CAS enabled, fetching a
git::source could fail withpermission deniedon.git/HEADor.git/config, sending Terragrunt back to the standard getter. It happened when the CAS store and the module's working directory sit on different filesystems, so the files are copied rather than hard-linked, and a read-only leftover from an interrupted run was in the way. Terragrunt now recovers from the leftover and completes the fetch.Reject
update_source_with_cason aterraformblock when CAS is disabledterragrunt stack generate --no-casnow fails when a generated unit'sterraformblock setsupdate_source_with_cas = true, instead of silently emitting the unit with its relativesourceunchanged. The relative source has no meaning once CAS is disabled, so the generated unit could not resolve its module. This matches the existing behavior for the same attribute onunitandstackblocks, and for aruninvoked with--no-cas.Apply
extra_argumentsenv vars when resolvingdependencyoutputsResolving a
dependencyblock's outputs now applies theenv_varsfrom the unit'sterraformextra_argumentsblocks whosecommandsincludeoutput.Resolve
dependencyoutputs for units whosebefore_hookreferences a dependencyResolving a unit's
dependencyoutputs no longer evaluates that unit'sterraformhooks, so abefore_hook(orafter_hook) that interpolates${dependency.<name>.outputs.<key>}no longer fails downstream units withThere is no variable named "dependency". Dependency output resolution still applies the unit'sextra_argumentsenv_varsandsource.Select units reading added or deleted glob files in Git-based filters
Git-based filters (for example
terragrunt run --all --filter '[HEAD^1...HEAD]' -- plan) now select units
that read an added or deleted file throughmark_glob_as_read, even when that file lives outside the unit's
own directory. Previously only modified files outside a unit reached those units; adding or deleting a file
the glob matched left the reading unit out of the run, so its real config change was skipped. Added files are
matched against the newer reference, and deleted files against the older one where the file still exists.mark_glob_as_readconstrains its walk to a boundarymark_glob_as_readnow confines glob expansion to a boundary directory. By default the boundary is the enclosing Git repository root; outside a Git repository it is unset. A pattern whose walk would begin outside the boundary returns an error instead of expanding.This bounds patterns that resolve higher than intended. For example,
"${local.dir}/{*.yaml}"becomes/{*.yaml}whenlocal.diris empty, which previously walked the entire filesystem. A? :conditional does not prevent this, because HCL evaluates both branches of a conditional before selecting one. Wrapping the call intrylets the error fall back to a default:locals { files = sort(try(mark_glob_as_read("${local.dir}/{*.yaml,*.yml,*.json}"), [])) }
Pass a leading
--terragrunt-boundaryargument to set the boundary explicitly, for example to scope the walk to a subdirectory or to widen it to the filesystem root:locals { scoped = mark_glob_as_read("--terragrunt-boundary=/etc/terragrunt", "/etc/terragrunt/{*.yaml}") all = mark_glob_as_read("--terragrunt-boundary=/", "/{*.yaml}") }
Scaffold only detects variables in the module directory
terragrunt scaffoldnow reads input variables from the module directory itself, matching what OpenTofu and Terraform load for a root module. Previously it scanned subdirectories too, sovariableblocks defined in nested modules or examples leaked into the scaffolded inputs even though the module never exposes them.Resolve interpolated object keys in autoinclude blocks
terragrunt stack generatenow resolves interpolated object keys inautoincludeblocks (for example
{ "${local.prefix}_key" = ... }), even when the value referencesdependency.*. Previously the generated
unit kept the key verbatim, leaking a stack-only reference that is not valid in the unit scope.Fix panic on non-string literal interpolation in
autoincludetemplatesterragrunt stack generateno longer panics when anautoincludetemplate interpolates a non-string literal (for example"${0}"or"${true}") alongside adependency.*reference. The interpolated literal is now rendered to its string form (${0}becomes0) and the dependency reference is preserved for the unit.Resolve transitive
autoincludedependencies on a stack directoryrun --allno longer fails with "does not contain a terragrunt.hcl file" when anautoincludedependency points at a stack directory (one holdingterragrunt.stack.hcl) and the unit is reached transitively through another unit. The dependency cycle check now skips a target with no unit config, matching the direct dependency case.🧪 Experiments Updated
Six experiments completed
The following experiments graduated to general availability in this release, and the features they gated are now enabled by default:
stack-dependenciescascatalog-redesignmark-many-as-readopt-out-authdag-queue-display
Each feature is described in the New Features section above.
The corresponding
--experimentflags (andTG_EXPERIMENTvalues) are no longer needed. Passing one still works, but emits a warning about the completed experiment, so you can drop it at your convenience.Thank you to everyone who ran these experiments early and filed the feedback that got them here.
⚙️ Process Updates
Immutable releases
Starting with this release, Terragrunt releases are published as immutable releases on GitHub. Once a release is published, its tag and assets can no longer be modified or deleted, so the binary you download is guaranteed to be the same binary that was uploaded when the release was published.
See the releases process documentation for details, and Verifying releases with the GitHub CLI for how to check a download against the release attestation.
Install script verifies release attestations
The install script now checks downloaded release assets against the release attestation that ships with immutable releases. For releases starting with
v1.1.0, when an authenticated GitHub CLI (v2.81.0 or later) is available, the script verifies the checksums file and the binary against the attestation before installing, and aborts if either does not match the published release. The check is skipped with a warning whenghis unavailable, too old, or unauthenticated. Use--no-verify-attestationto opt out.Pull Requests
✨ Features
🐛 Bug Fixes
- fix: autoincludes variables interpolation by @denis256 in #6318
- fix: generate overwrite of read-only CAS-materialized files by @denis256 in #6329
- fix: CAS integration with committed module lockfiles by @yhakbar in #6330
- fix: improved resolving of complex objects in keys by @denis256 in #6317
- fix: Fixing
update_source_with_casintegration with--no-casby @yhakbar in #6363 - fix: Adding support for adding/deleting files in Git diffs by @yhakbar in #6352
- fix: Adding
--terragrunt-boundarytomark_glob_as_readby @yhakbar in #6351 - fix: Only detect variables in root directory of module by @yhakbar in #6381
- fix: resolve transitive autoinclude dependency on a stack directory by @denis256 in #6389
- fix: apply extra_arguments env_vars when resolving dependency outputs by @denis256 in #6396
- fix: Fixing indenter style by @yhakbar in #6402
- fix: Fixing legacy Windows per-drive key for environment variables by @yhakbar in #6412
- fix: Fixing Git materialization race HEAD update by @yhakbar in #6411
- fix: fixed reference of dependency outputs in terraform hooks by @denis256 in #6423
- fix: Fixing deleted changed files by @yhakbar in #6424
📖 Documentation
- docs: Documenting
--parallelismtweaking considerations better by @yhakbar in #6313 - docs: Adding TGS 'Terragrunt at scale' page by @yhakbar in #6307
- docs:
v1.1.0changelog polish by @yhakbar in #6333 - docs: Improving performance docs by @yhakbar in #6332
- docs: Adding immutable releases docs by @yhakbar in #6337
- docs: Cleaning up 1.1.0 docs by @yhakbar in #6339
- docs: Updating provider size claims for provider cache server docs by @yhakbar in #6341
- docs: Documenting Discovery as a term by @yhakbar in #6359
- docs: Cleaning up catalog tabs docs by @yhakbar in #6369
🧹 Chores
- chore: marking as completed stack dependencies experiment by @denis256 in #6249
- chore: Completing
mark-many-as-readexperiment by @yhakbar in #6310 - chore: Completing
casexperiment by @yhakbar in #6254 - chore: Addressing feedback from #6254 by @yhakbar in #6324
- chore: Completing
dag-queue-displayexperiment by @yhakbar in #6320 - chore: Completing
opt-out-authexperiment by @yhakbar in #6321 - chore: Dropping
go-gitby @yhakbar in #6325 - chore: Addressing PR #6325 feedback by @yhakbar in #6335
- chore: bump cicd to use opentofu 1.12.2 by @denis256 in #6343
- chore: Signing GHA update by @denis256 in #6340
- chore: multiple dependencies update by @denis256 in #6356
- chore: Updating CI w/ Terragrunt guide to have more accurate screenshots by @yhakbar in #6357
- chore: drop usage of github.com/NYTimes/gziphandler by @denis256 in #6364
- chore(deps): bump astro from 6.3.2 to 6.4.6 in /docs by @dependabot[bot] in #6366
- chore: Completing
catalog-redesignexperiment by @yhakbar in #6271 - chore: Bumping JS dependencies by @yhakbar in #6368
- chore: Adding release attestation verification to install script by @yhakbar in #6344
- chore: Addressing weekly test stats by @yhakbar in #6354
- chore: Addressing #6351 feedback by @yhakbar in #6373
- chore: Adding mise.toml lockfile by @yhakbar in #6372
- chore: Making progress on
lllby @yhakbar in #6377 - chore: autoinclude fuzzing tests improvements by @denis256 in #6342
- chore: Expand pure testing through venv by @yhakbar in #6090
- chore: Dropping
TestWindowsTflintIsInvokedby @yhakbar in #6382 - chore: Continuing with progress on
lll#2 by @yhakbar in #6385 - chore: added CICD guard for detecting not run tests by @denis256 in #6383
- chore: Addressing weekly tests stats (2026-06-22) by @yhakbar in #6390
- chore: Addressing #6390 feedback by @yhakbar in #6391
- chore: go deps update by @denis256 in #6392
- chore: Running
go fix ./...by @yhakbar in #6398 - chore(deps): bump actions/checkout from 6.0.2 to 7.0.0 by @dependabot[bot] in #6394
- chore: Update cfg locking for units by @yhakbar in #6401
- chore: Move log flags off writers by @yhakbar in #6403
- chore: Adding env and writers to venv by @yhakbar in #6404
- chore: aws-sdk-go-v2/service/s3 upgrade by @denis256 in #6413
- chore: Downgrading
renderlog to adebugby @yhakbar in #6429 - chore: Engine per unit shutdown by @yhakbar in #6426
📝 Other Changes
- Adding Terragrunt Patterns section by @karlcarstensen in #6379
- Add FAQ section for docs.terragrunt.com by @karlcarstensen in #6378
- Fixing codespell lint error and adding codespell lint commands by @karlcarstensen in #6386
-
v1.1.0-rc3.0.20260630193753-a2a294d1af8c30 Jun 2026 pre-releaseNothing published for this version
-
v1.1.0-rc3.0.20260629170823-da39318e125f29 Jun 2026 pre-releaseNothing published for this version
-
v1.1.0-rc3.0.20260626190549-3abafb9ade1126 Jun 2026 pre-releaseNothing published for this version
-
v1.1.0-rc326 Jun 2026 pre-releaseRelease notes
Open source →🎉 v1.1.0 Release Candidate
This is the third release candidate for Terragrunt v1.1.
It carries the same six completed experiments as v1.1.0-rc2, plus bug fixes for those experiments and improvements to how releases are published and verified.
This release completes the following experiments:
stack-dependenciescascatalog-redesignmark-many-as-readopt-out-authdag-queue-display
Future release candidates for v1.1.0 will include bug fixes related to these experiments or other urgent bug fixes as necessary, and documentation improvements.
Please try out this release candidate in lower environments and share your feedback in the associated GitHub discussion.
🆕 Changes since rc2
🐛 Bug Fixes
-
Dependency output resolution: Resolving a
dependencyblock's outputs now applies theenv_varsfrom the unit'sextra_argumentsblocks whosecommandsincludeoutput(#6396). -
Stack autoinclude: Transitive
autoincludedependencies that point at a stack directory now resolve correctly (#6389). -
Scaffold variable detection:
terragrunt scaffoldnow reads input variables from the module directory only, sovariableblocks in nested modules or examples no longer leak into the scaffolded inputs (#6381).
💡 Tips Added
- Nested stack generation: When a non-glob
| type=stackfilter generates a stack but leaves its nested stacks ungenerated,terragrunt stack generatenow prints a tip showing how to generate them too (#6387).
📚 Documentation
✨ New Features
Stack dependencies
A stack generates a tree of units from a single
terragrunt.stack.hclfile. Wiring one of those units to another used to mean definingdependencyblocks in your catalog and threading dependency paths throughvalues. Stack dependencies let you declare those relationships up front instead.Add an
autoincludeblock inside aunitorstackblock, and Terragrunt generates a partial configuration (aterragrunt.autoinclude.hclfile) next to the generatedterragrunt.hclorterragrunt.stack.hclthat's automatically merged into the unit or stack definition. The newunit.<name>.pathandstack.<name>.pathreferences resolve to generated paths, so you don't have to hardcode them:# 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 } } }
Anything that's valid in a unit configuration is valid in its
autoincludeblock, so you can also patch catalog units with configuration they don't ship with, like retry rules:# terragrunt.stack.hcl 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 } } } }
The same works for nested stacks: an
autoincludeblock inside astackblock patches the generatedterragrunt.stack.hcl, so you can, for example, add an extra unit to one environment without forking the stack in your catalog.Stack configurations also gained two capabilities along the way:
includeblocks now work interragrunt.stack.hclfiles, so shared stack configuration can live in a parent folder.dependencyblocks can target stack directories, and the run queue expands them to the units inside. Note that this relationship only goes one way: units can depend on stacks, but stacks cannot depend on stacks or units.
See the stacks documentation for the full reference. Previously gated behind the
stack-dependenciesexperiment, all of this is now enabled by default.Content Addressable Store (CAS)
The Content Addressable Store (CAS) deduplicates source downloads across configurations. It addresses repositories and modules by their content, stores them locally, and serves later requests from that local store instead of repeating the fetch. This speeds up catalog cloning, OpenTofu/Terraform source fetching, and stack generation, and identical files occupy disk space once regardless of how many configurations use them.
The CAS is no longer limited to Git. It also deduplicates HTTP, Amazon S3, Google Cloud Storage, Mercurial, and SMB sources, along with OpenTofu/Terraform registry sources fetched via
tfr://. See supported sources for how each one resolves and deduplicates content.CAS is enabled by default. Use the
--no-casflag (orTG_NO_CAS=true) to opt out of it for a run:terragrunt run --all --no-cas -- plan
Two new attributes give you finer control, and both default to off:
-
update_source_with_casmakes a generated stack self-contained. Set it on aunit,stack, orterraformblock with a relativesource, andterragrunt stack generaterewrites that source into a content-addressedcas::reference, so the generated tree no longer depends on the surrounding repository layout. Catalog authors can keep relative paths in their sources and still ship a portable, reproducible stack:# stacks/networking/terragrunt.stack.hcl unit "vpc" { source = "../..//units/vpc" path = "vpc" update_source_with_cas = true }
After
terragrunt stack generate, the relative path is replaced by a reference to the exact tree the CAS stored:# Generated output unit "vpc" { source = "cas::sha1:f39ea0ebf891c9954c89d07b73b487ff938ef08b" path = "vpc" update_source_with_cas = true }
-
mutablecontrols how the CAS places fetched content on disk. By default, the CAS hard links files from its shared store into.terragrunt-cacheand marks them read-only, which is fast and uses no extra space, but means the files can't be edited in place. Setmutable = trueon aterraformblock to copy the content instead, making the working tree safe to edit at the cost of extra I/O and disk space:# units/vpc/terragrunt.hcl terraform { source = "github.com/acme/catalog//modules/vpc" mutable = true }
Previously gated behind the
casexperiment, the CAS no longer requires--experiment cas.Redesigned
terragrunt catalogThe
catalogcommand has been redesigned. It now starts without any configuration, discovers components across your catalog repositories in the background, and streams them into the TUI as they're found.Discovery is no longer limited to a
modules/directory; components can live anywhere in a catalog repository. To control what gets discovered, add a.terragrunt-catalog-ignorefile with.gitignore-style globs for the paths you want filtered out.Components in the TUI now carry metadata to help you navigate a large catalog: each one shows a kind label (
template,stack,unit, ormodule) and optional tags defined in the front-matter of itsREADME.md. From the component list, presssto open a new screen that interactively collects the values used to scaffold the component into your repository.Previously gated behind the
catalog-redesignexperiment, the redesigned catalog is now the defaultterragrunt catalogexperience.Reading detection for local module sources
Terragrunt can select units by the files they read, which is the basis of change-based runs in CI. Previously, pointing a unit's
terraformblock at a local directory didn't mark the files inside that directory as read, so a change to the module wouldn't select the unit.When a unit's source is a local module, Terragrunt now records the module's
*.tf,*.tf.json,*.hcl,*.tofu, and*.tofu.jsonfiles as read by that unit, so--filter 'reading=<path>'and--queue-include-units-readingselect the unit when a module file changes:terragrunt run --all --filter 'reading=./modules/vpc/main.tf' -- planFor files that reading detection doesn't track on its own, the new
mark_glob_as_read()HCL function expands a glob and marks every matching file as read in one call:locals { configs = mark_glob_as_read("${get_terragrunt_dir()}/config/{*.yaml,**/*.yaml}") }
Existing pipelines built on
--queue-include-units-readingorreading=filters may select more units than before, because changes to local module files now count as reads. Previously gated behind themark-many-as-readexperiment, these behaviors no longer require--experiment mark-many-as-read.Skip auth during discovery with
--no-discovery-auth-provider-cmdBy default, Terragrunt runs your
--auth-provider-cmdonce for every unit it discovers, so HCL functions that need credentials resolve correctly during parsing. In a large repository, that can mean hundreds of invocations before any unit runs, which can dominate wall-clock time on change-based runs.The
--no-discovery-auth-provider-cmdflag (env:TG_NO_DISCOVERY_AUTH_PROVIDER_CMD) skips those invocations during the discovery phase, leaving auth to run only for the units that actually execute:terragrunt run --all \ --no-discovery-auth-provider-cmd \ --queue-include-units-reading=./changed-file.txt \ -- plan
Warning
Use this only when you know parsing resolves without credentials. Units whose configuration depends on values from
--auth-provider-cmdduring discovery (for example, viaget_aws_account_id()) will fail to parse when the flag is set.Previously gated behind the
opt-out-authexperiment, the flag now works without--experiment opt-out-auth.Run queue displayed as a dependency tree
Before a
run --all, Terragrunt lists the units it's about to run. That list now renders as a dependency tree by default instead of a flat list, with units nested under their dependencies, so the run order and the relationships between units are visible before anything executes:The following units will be run, starting with dependencies and then their dependents: . ├── monitoring ╰── vpc ╰── database ╰── backend-appThe header adapts to direction: dependencies come before dependents on apply, and the order reverses on destroy.
Previously gated behind the
dag-queue-displayexperiment, the tree display no longer requires--experiment dag-queue-display.💡 Tips Added
Tip when filtering a stack leaves nested stacks ungenerated
terragrunt stack generate --filter './my-stack | type=stack'generates only the selected
stack, not the nested stacks it contains, which can be surprising for a stack of stacks.
When a non-glob| type=stackfilter leaves a stack's nested stacks ungenerated, Terragrunt
now prints a tip showing how to generate them too, for example
--filter './my-stack | type=stack' --filter './my-stack/** | type=stack'.🐛 Bug Fixes
Fix
permission deniedwhen generated files overwrite CAS-materialized filesWith the CAS enabled, Terragrunt fetches sources as read-only files. Writing a generated file over one of them no longer fails with
permission denied:- Files from
generateblocks withif_exists = "overwrite", when the module ships the target file (for example, its ownversions.tf). terragrunt.values.hcl, when the unit or stack source already contains one.terragrunt.autoinclude.hcl, when the unit or stack source already contains one..terraform.lock.hcl, when the provider cache server updates a committed lock file duringinit -upgrade.
In each case, the read-only file is replaced with a writable one, and the shared CAS store is never modified.
Reject
update_source_with_cason aterraformblock when CAS is disabledterragrunt stack generate --no-casnow fails when a generated unit'sterraformblock setsupdate_source_with_cas = true, instead of silently emitting the unit with its relativesourceunchanged. The relative source has no meaning once CAS is disabled, so the generated unit could not resolve its module. This matches the existing behavior for the same attribute onunitandstackblocks, and for aruninvoked with--no-cas.Apply
extra_argumentsenv vars when resolvingdependencyoutputsResolving a
dependencyblock's outputs now applies theenv_varsfrom the unit'sterraformextra_argumentsblocks whosecommandsincludeoutput.Select units reading added or deleted glob files in Git-based filters
Git-based filters (for example
terragrunt run --all --filter '[HEAD^1...HEAD]' -- plan) now select units
that read an added or deleted file throughmark_glob_as_read, even when that file lives outside the unit's
own directory. Previously only modified files outside a unit reached those units; adding or deleting a file
the glob matched left the reading unit out of the run, so its real config change was skipped. Added files are
matched against the newer reference, and deleted files against the older one where the file still exists.mark_glob_as_readconstrains its walk to a boundarymark_glob_as_readnow confines glob expansion to a boundary directory. By default the boundary is the enclosing Git repository root; outside a Git repository it is unset. A pattern whose walk would begin outside the boundary returns an error instead of expanding.This bounds patterns that resolve higher than intended. For example,
"${local.dir}/{*.yaml}"becomes/{*.yaml}whenlocal.diris empty, which previously walked the entire filesystem. A? :conditional does not prevent this, because HCL evaluates both branches of a conditional before selecting one. Wrapping the call intrylets the error fall back to a default:locals { files = sort(try(mark_glob_as_read("${local.dir}/{*.yaml,*.yml,*.json}"), [])) }
Pass a leading
--terragrunt-boundaryargument to set the boundary explicitly, for example to scope the walk to a subdirectory or to widen it to the filesystem root:locals { scoped = mark_glob_as_read("--terragrunt-boundary=/etc/terragrunt", "/etc/terragrunt/{*.yaml}") all = mark_glob_as_read("--terragrunt-boundary=/", "/{*.yaml}") }
Scaffold only detects variables in the module directory
terragrunt scaffoldnow reads input variables from the module directory itself, matching what OpenTofu and Terraform load for a root module. Previously it scanned subdirectories too, sovariableblocks defined in nested modules or examples leaked into the scaffolded inputs even though the module never exposes them.Resolve interpolated object keys in autoinclude blocks
terragrunt stack generatenow resolves interpolated object keys inautoincludeblocks (for example
{ "${local.prefix}_key" = ... }), even when the value referencesdependency.*. Previously the generated
unit kept the key verbatim, leaking a stack-only reference that is not valid in the unit scope.Fix panic on non-string literal interpolation in
autoincludetemplatesterragrunt stack generateno longer panics when anautoincludetemplate interpolates a non-string literal (for example"${0}"or"${true}") alongside adependency.*reference. The interpolated literal is now rendered to its string form (${0}becomes0) and the dependency reference is preserved for the unit.Resolve transitive
autoincludedependencies on a stack directoryrun --allno longer fails with "does not contain a terragrunt.hcl file" when anautoincludedependency points at a stack directory (one holdingterragrunt.stack.hcl) and the unit is reached transitively through another unit. The dependency cycle check now skips a target with no unit config, matching the direct dependency case.🧪 Experiments Updated
Six experiments completed
The following experiments graduated to general availability in this release, and the features they gated are now enabled by default:
stack-dependenciescascatalog-redesignmark-many-as-readopt-out-authdag-queue-display
Each feature is described in the New Features section above.
The corresponding
--experimentflags (andTG_EXPERIMENTvalues) are no longer needed. Passing one still works, but emits a warning about the completed experiment, so you can drop it at your convenience.Thank you to everyone who ran these experiments early and filed the feedback that got them here.
⚙️ Process Updates
Immutable releases
Starting with this release, Terragrunt releases are published as immutable releases on GitHub. Once a release is published, its tag and assets can no longer be modified or deleted, so the binary you download is guaranteed to be the same binary that was uploaded when the release was published.
See the releases process documentation for details, and Verifying releases with the GitHub CLI for how to check a download against the release attestation.
Install script verifies release attestations
The install script now checks downloaded release assets against the release attestation that ships with immutable releases. For releases starting with
v1.1.0, when an authenticated GitHub CLI (v2.81.0 or later) is available, the script verifies the checksums file and the binary against the attestation before installing, and aborts if either does not match the published release. The check is skipped with a warning whenghis unavailable, too old, or unauthenticated. Use--no-verify-attestationto opt out.Pull Requests
✨ Features
🐛 Bug Fixes
- fix: autoincludes variables interpolation by @denis256 in #6318
- fix: generate overwrite of read-only CAS-materialized files by @denis256 in #6329
- fix: CAS integration with committed module lockfiles by @yhakbar in #6330
- fix: improved resolving of complex objects in keys by @denis256 in #6317
- fix: Fixing
update_source_with_casintegration with--no-casby @yhakbar in #6363 - fix: Adding support for adding/deleting files in Git diffs by @yhakbar in #6352
- fix: Adding
--terragrunt-boundarytomark_glob_as_readby @yhakbar in #6351 - fix: Only detect variables in root directory of module by @yhakbar in #6381
- fix: resolve transitive autoinclude dependency on a stack directory by @denis256 in #6389
- fix: apply extra_arguments env_vars when resolving dependency outputs by @denis256 in #6396
- fix: Fixing indenter style by @yhakbar in #6402
📖 Documentation
- docs: Documenting
--parallelismtweaking considerations better by @yhakbar in #6313 - docs: Adding TGS 'Terragrunt at scale' page by @yhakbar in #6307
- docs:
v1.1.0changelog polish by @yhakbar in #6333 - docs: Improving performance docs by @yhakbar in #6332
- docs: Adding immutable releases docs by @yhakbar in #6337
- docs: Cleaning up 1.1.0 docs by @yhakbar in #6339
- docs: Updating provider size claims for provider cache server docs by @yhakbar in #6341
- docs: Documenting Discovery as a term by @yhakbar in #6359
- docs: Cleaning up catalog tabs docs by @yhakbar in #6369
🧹 Chores
- chore: marking as completed stack dependencies experiment by @denis256 in #6249
- chore: Completing
mark-many-as-readexperiment by @yhakbar in #6310 - chore: Completing
casexperiment by @yhakbar in #6254 - chore: Addressing feedback from #6254 by @yhakbar in #6324
- chore: Completing
dag-queue-displayexperiment by @yhakbar in #6320 - chore: Completing
opt-out-authexperiment by @yhakbar in #6321 - chore: Dropping
go-gitby @yhakbar in #6325 - chore: Addressing PR #6325 feedback by @yhakbar in #6335
- chore: bump cicd to use opentofu 1.12.2 by @denis256 in #6343
- chore: Signing GHA update by @denis256 in #6340
- chore: multiple dependencies update by @denis256 in #6356
- chore: Updating CI w/ Terragrunt guide to have more accurate screenshots by @yhakbar in #6357
- chore: drop usage of github.com/NYTimes/gziphandler by @denis256 in #6364
- chore(deps): bump astro from 6.3.2 to 6.4.6 in /docs by @dependabot[bot] in #6366
- chore: Completing
catalog-redesignexperiment by @yhakbar in #6271 - chore: Bumping JS dependencies by @yhakbar in #6368
- chore: Adding release attestation verification to install script by @yhakbar in #6344
- chore: Addressing weekly test stats by @yhakbar in #6354
- chore: Addressing #6351 feedback by @yhakbar in #6373
- chore: Adding mise.toml lockfile by @yhakbar in #6372
- chore: Making progress on
lllby @yhakbar in #6377 - chore: autoinclude fuzzing tests improvements by @denis256 in #6342
- chore: Expand pure testing through venv by @yhakbar in #6090
- chore: Dropping
TestWindowsTflintIsInvokedby @yhakbar in #6382 - chore: Continuing with progress on
lll#2 by @yhakbar in #6385 - chore: added CICD guard for detecting not run tests by @denis256 in #6383
- chore: Addressing weekly tests stats (2026-06-22) by @yhakbar in #6390
- chore: Addressing #6390 feedback by @yhakbar in #6391
- chore: go deps update by @denis256 in #6392
- chore: Running
go fix ./...by @yhakbar in #6398 - chore(deps): bump actions/checkout from 6.0.2 to 7.0.0 by @dependabot[bot] in #6394
- chore: Update cfg locking for units by @yhakbar in #6401
- chore: Move log flags off writers by @yhakbar in #6403
- chore: Adding env and writers to venv by @yhakbar in #6404
📝 Other Changes
- Adding Terragrunt Patterns section by @karlcarstensen in #6379
- Add FAQ section for docs.terragrunt.com by @karlcarstensen in #6378
- Fixing codespell lint error and adding codespell lint commands by @karlcarstensen in #6386
-
v1.1.0-rc2.0.20260624104514-334d456846ef24 Jun 2026 pre-releaseNothing published for this version
-
v1.1.0-rc2.0.20260623203131-71322d30eddf23 Jun 2026 pre-releaseNothing published for this version
-
v1.1.0-rc2.0.20260622193936-23878ba5062322 Jun 2026 pre-releaseNothing published for this version
-
v1.1.0-rc2.0.20260619120706-f1d1b6b1263d19 Jun 2026 pre-releaseNothing published for this version