Use Terramate Stacks and Changed-Only Runs to Shrink Terraform Blast Radius
Make Terraform deploy graphs explicit with Terramate stacks, render shared backend and provider config via code generation, and run only changed stacks to cut CI time and blast radius.
18 Jul 2025, 02:20 UTC

Multi-environment Terraform repos tend to grow into an implicit mess. Folders imply meaning, backend.tf and provider.tf get copy-pasted per environment, and CI runs plan or apply across everything because it is hard to know what actually changed. The useful takeaway is to make the deploy graph explicit and run only what changed: Terramate treats each deployable unit as a stack, renders shared configuration with code generation, and executes commands only in stacks affected by the current git change.
The problem is implicit organization and duplicated config
When environments live under envs/dev/, envs/prod/ and services are nested inside, it is easy to drift. A backend block for S3/DynamoDB is duplicated, provider versions are edited in multiple places, and a small change to a shared module triggers a full-repo plan. That increases CI time and the blast radius of an apply.
Terramate does not replace Terraform or OpenTofu. It orchestrates them. A stack is a directory that contains Terraform code and a stack.tm.hcl file. That file makes the unit addressable and lets you declare metadata and dependencies. Globals defined in globals.tm.hcl cascade down the directory tree and can be overridden per directory, giving a configuration layer that sits above Terraform variables.
Code generation removes copy-pasted backend and provider files
Instead of maintaining backend.tf in every stack, define it once and render it. A generate_hcl block in a parent directory can emit files into each stack at generation time. The generated files are ordinary HCL that Terraform reads.
Example layout:
repo/
globals.tm.hcl
generate/
backend.tm.hcl
envs/
dev/
network/
stack.tm.hcl
main.tf
app/
stack.tm.hcl
main.tf
globals.tm.hcl at repo root:
globals {
environment = "dev"
aws_region = "us-east-1"
backend_bucket = "-tfstate-dev"
}
generate/backend.tm.hcl:
generate_hcl "backend" {
content {
terraform {
backend "s3" {
bucket = globals.backend_bucket
key = "${path.basename}/terraform.tfstate"
region = globals.aws_region
dynamodb_table = "terraform-locks"
}
}
}
}
Run generation from the repo root with write access to the stacks:
terramate generate
Check the result by inspecting the generated backend.tf inside each stack and confirming the bucket and region resolve from globals. If you edit globals or the generate block, you must re-run terramate generate before plan/apply, otherwise Terraform will see stale files and produce confusing diffs.
Changed-only execution limits blast radius
Terramate integrates change detection with git. terramate run --changed discovers stacks whose own files or transitive module dependencies changed in the current branch compared to the base. Only those stacks are passed to the subcommand, e.g., terraform plan.
A stack can declare ordering:
// envs/dev/network/stack.tm.hcl
stack {}
// envs/dev/app/stack.tm.hcl
stack {
wants = ["../network"]
}
This expresses that the app stack depends on the network stack. Terramate can order execution accordingly. In CI, run from the repo root with read access to the git history and the stacks:
terramate run --changed terraform plan
Verify the behavior in a test repo with two stacks, modify a file in one stack, and confirm the other stack is not invoked. Test change detection with your actual clone depth and merge strategy before relying on it for production applies, as shallow clones and unusual merges can cause missed changes.
Trade-offs and limitations
Code generation adds a build step. Teams must agree to commit generated files or generate them reliably in CI. HCL-based globals differ from Terraform's variable model, so there is a learning curve and a risk of over-abstracting early. Change detection depends on git history being present and on configured rules for what counts as a change; it is not magic.
Terramate Cloud adds drift detection and deployment visibility, but the CLI features described here work standalone and support gradual adoption.
Start small: pick two related stacks, centralize backend and provider via generate_hcl, define globals for environment, and run terramate run --changed locally. Inspect generated files and validate that only changed stacks execute. Once that pattern is reliable, expand the explicit dependency graph and keep generation as part of your pre-plan checks.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.