Scoping Terraform CI with Terramate Stacks and Change Detection
Running every Terraform root module on every commit wastes CI and hides risk. Terramate stacks plus change detection let you plan only affected code—if your Git metadata is sound.
07 Sept 2026, 05:33 UTC

Running every root module in a Terraform monorepo on every commit is a tax you pay in minutes and risk. A change to one application stack triggers plans for unrelated networks, databases, and shared modules. The pipeline slows down, reviewers lose signal about what actually changed, and the blast radius of a mistake stays hidden until apply. Terramate approaches this by turning each deployable directory into an explicit stack and using Git-aware change detection to run only affected code in dependency order.
Stacks replace implicit directory conventions
In a standard Terraform repository, any directory with a .tf file can be a root module, but nothing declares it as a deployable unit. Terramate makes that declaration explicit: a stack.tm.hcl file marks a directory as a stack. A root terramate.tm.hcl holds shared configuration. Once stacks exist, Terramate builds a graph across them, so orchestration can run init, plan, and apply in a computed order rather than as an unordered CI matrix.
Change detection scopes work to the affected set
Terramate compares the current checkout against a Git base—such as a pull request merge-base or a target branch—to identify changed stacks. That affected set becomes the input for a run. The exact flags and whether dependent stacks are pulled in are version-sensitive, so confirm behavior with terramate run --help in the same container image your CI uses.
A worked dry-run example
Suppose you have two stacks: infra/network and infra/app. Each contains a stack.tm.hcl. You open a pull request that modifies only files under infra/app.
# Run locally from the repository root after checking out the PR branch
terramate run --terraform-plan --dry-run
Expected check: the selected set should contain infra/app and any stacks configured as its dependents. To see the full inventory regardless of changes, run:
terramate list
Comparing the two outputs is the fastest way to confirm that change detection is working as intended before you wire it into CI.
CI conditions that break detection
Change detection is only as reliable as the Git metadata available to the job. Shallow clones and detached HEAD states can cause Terramate to under-report changed stacks. In GitHub Actions, fetch full history and the base ref:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}
Permissions: the checkout step typically needs contents: read. Replace the ref value with your provider's equivalent if you are not using GitHub Actions. Risk: if fetch-depth is not zero, the base ref may be unavailable and change detection can silently skip stacks.
Start with --dry-run and a plan-only job. Keep safeguards enabled so that a dirty working tree or stale synchronization with Terramate Cloud fails loudly rather than silently skipping stacks.
Trade-offs and limitations
Boundary sensitivity:If a shared module lives outside any stack, changes to it may not mark dependent stacks unless your configuration explicitly links them.Orchestration overhead:Many small stacks increase scheduling and coordination cost. The graph is useful, but hundreds of tiny stacks can add latency without reducing total Terraform work.External dependencies:Terramate Cloud features such as drift detection, inventory, and notifications introduce an external service. Their availability, semantics, and pricing are time-sensitive and require current verification.State safety is unchanged:Orchestration does not replace remote state, locking, or least-privilege credentials. A perfectly scoped plan can still apply the wrong change if state handling is weak.
An incremental adoption path
- Pick one critical stack and add its
stack.tm.hcl. - Run
terramate run --terraform-plan --dry-runin an existing pipeline and record the selected set. - Verify the selected set matches your expectation against
terramate list. - Expand stack-by-stack. Add
applyonly after the selected set is stable and your CI clone provides full history.
Treat Terramate as a way to make deployment boundaries explicit and CI scoped, not as a replacement for Terraform's own safety mechanisms. Verify flags and Cloud behavior against your installed version before relying on them.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.