Skip to main content
A platform engineering team manages dozens of stacks across multiple services. Every stack needs the same S3 backend. Every stack should tag resources with team and repo. Every stack should use at least AWS provider v6. Without a hierarchy mechanism, you duplicate these declarations in every stack’s ubx.iac file — and when the backend bucket name changes, you update it in every file. ubx.root.iac solves this: place it at the repository root and ubx automatically discovers it when validating or applying any child project in the directory tree below it. Backend config, provider version constraints, and shared tags cascade down to every child stack. Children inherit everything from root and can override any attribute locally — root wins only on attributes the child doesn’t declare.

What you’ll learn

  • How ubx.root.iac provides shared config inherited by all child stacks
  • The merge rules: child wins on overlap, root fills in what child doesn’t declare
  • How ubx validate --all validates the entire hierarchy at once

Why this matters

Code hierarchy is the difference between a platform with consistent defaults and a platform where every team independently decides their backend bucket name. ubx.root.iac is a single source of truth for org-wide infrastructure conventions — backend, provider versions, mandatory tags — that every stack inherits automatically.

The source code

# No backend block — inherited from ubx.root.iac.
# No default_tags — team and repo tags from ubx.root.iac are applied automatically.
unit "aws_s3_bucket_v2" "api_assets" {
  bucket = "myapp-api-${input.env}-assets"
}

unit "aws_sqs_queue" "api_jobs" {
  name = "myapp-api-${input.env}-jobs"
}

How it works

1

ubx walks up from the child directory to find ubx.root.iac

When you run ubx validate from services/api/, ubx walks up the directory tree looking for ubx.root.iac. It finds it two levels up at the repository root. The root discovery happens before compilation — the root config is merged into the child’s compile context.
2

Root and child configs are merged

The merger applies root config as defaults. Child config overrides root on any attribute that appears in both. Tags are deep-merged: team = "platform" from root plus any child-declared tags coexist. Provider version constraints: child wins if it declares the same provider; root fills in providers the child doesn’t declare.
3

ubx validate --all discovers and validates all children

From the repository root, ubx validate --all scans for all directories containing a ubx.iac file, applies the root config to each, and validates them all. 2 passed · 0 failed means every child stack in the hierarchy is valid. This is the CI command for a monorepo.

Merge rules

Config typeMerge behaviour
tags {}Deep merge — child tags and root tags coexist; child wins on overlapping keys
provider versionChild wins if same provider is declared in both; root fills in providers child doesn’t declare
backendChild’s backend block takes precedence; root backend is ignored if child declares one
runtimeChild’s ubx.iac runtime wins; root can’t override child runtime

Common mistakes

ubx.root.iac is discovered by walking up from the child directory — it must be an ancestor of the child, not a sibling. If you place ubx.root.iac in services/ rather than at the repo root, only stacks directly inside services/ will discover it; stacks in services/api/ will not.
Run ubx validate --all from the repo root in CI rather than validating each stack separately. It applies root config to every child and validates the full hierarchy in one command — catching root config errors and child compatibility issues simultaneously.

Run it

# From repo root:
ubx validate --all                    # validates all child stacks with root config

# From a child directory:
cd services/api
ubx validate                          # validates api stack, inherits root config
ubx plan                              # requires AWS credentials

What you learned

ubx.root.iac at the repo root provides shared config inherited by all child stacks below it
Child config wins on overlap; root fills in attributes the child doesn’t declare
ubx validate --all from the root validates every child stack in the hierarchy at once

Next steps

Multi-runtime compilation

Compile the same .iac source to TypeScript, Python, or Go

ubx block reference

Full ubx block syntax and all configuration options