Terraform Guide

Terraform monorepo diagram: every stack on one canvas, not nineteen screenshots

By the engineer who builds Datadef, from client work on data platforms · Reviewed August 21, 2026

A Terraform monorepo is the layout most platform teams land on: shared modules in one directory, root modules per component or per environment, one repository, one review process. It is also the layout that breaks every visualization tool built around a working directory, because those tools draw one root module at a time and a monorepo has nineteen.

7 min readFor platform teams with many root modules in a single repository

See it as a diagram

Everything below, as a diagram you can edit. Describe yours and see it in seconds.

144/20003 credits left
Try:

No account needed · Editable canvas, not a picture

Why per-directory tools cannot see a monorepo

terraform graph, tofu graph, and most CLI visualizers operate on an initialized working directory. Point one at the repository root of a monorepo and it has nothing to do, because the root is not a root module. Point it at each stack in turn and you get nineteen DOT files, nineteen layout fights, and a folder of images that nobody assembles into an answer.

The assembly is the whole job. What a reader wants from a monorepo diagram is the relationship between the stacks and the shared modules: which components exist, which of them lean on the same networking module, where the platform layer stops and the product layer starts.

Finding the stacks without running anything

The rule is mechanical. A directory of .tf files is a module unit. A unit that no other unit names as a module source is a root module, which is a thing you can apply and which owns a state. Everything under modules/ that gets called with a relative source is a part. Backend declarations confirm the reading, since each backend block marks a separate state.

Directories whose path contains a segment reading as examples, tests, test-fixtures, fixtures, e2e, wrappers, or ci are set aside when real stacks exist elsewhere, so a repository that ships usage examples next to its platform does not get its diagram taken over by them. When the examples are the only root modules, which is the shape of a module-library repo, the module itself is promoted to be the subject instead.

# every directory that declares a backend, one line each
grep -rl 'backend "' --include='*.tf' . | xargs -n1 dirname | sort -u

# every module call and where it points
grep -rn -A3 '^module "' --include='*.tf' . | grep -E 'module "|source *='

# where the environments live
find . -name '*.tfvars*' | sort

The node budget grows with the stack count

Curation is what makes a monorepo diagram readable, and a fixed budget would ruin it. A normal repository targets around forty drawn nodes. Above eight stacks the target rises to five nodes per stack, capped at seventy-two, because nineteen small architectures at three nodes each read perfectly well as a grid of zones while forcing them into forty would gut every one of them.

Inside a module the cap is eight drawn nodes; the remaining singles fold into one Supporting resources node carrying its count. Repeated minor types collapse into a single node with the count and the member names, and identity wiring, role assignments, grants, policy attachments, is summarized in a note rather than drawn. The plan is built twice when it has to be: a first pass keeps lone minor resources, and if the canvas still exceeds the target the whole plan is rebuilt in a strict pass where those fold in too. The full inventory stays available in the generated module reference table.

The result is a canvas where each stack is a zone you can find by name, sized by how much it actually contains. That is a different artifact from the per-stack detail diagram, and both come from the same connection.

Environment-first and component-first both parse

The two common monorepo layouts are environment-first, where dev, staging, and prod each hold their own configuration and backend, and component-first, where directories are per service with a tfvars file per environment. Environment-first produces one stack per environment directory. Component-first produces one stack per component, with per-environment tfvars parsed into real counts on the nodes, so a resource created ten times in dev and once in prod is one node stating both numbers rather than ten boxes.

Three practical limits are worth knowing before you connect a very large repository. The sync fetches up to 400 .tf files and 500 blobs in total including tfvars, any single file over 400KB is skipped, and .terraform directories are never entered. Repositories past those limits are read up to the limit rather than refused, and the generated doc still lists every stack it found. For the environment side of this, see multi-environment Terraform on one diagram.

FAQ

How many Terraform stacks can one diagram hold?

The drawn-node target is about forty for an ordinary repository and rises above eight stacks to five nodes per stack, capped at seventy-two. In practice that means a repository of nineteen small root modules renders as a readable grid of nineteen zones rather than a compressed blob.

What counts as a stack?

A directory of .tf files that no other directory references as a module source. That is the same rule Terraform itself follows for a root module, so it holds regardless of how the folders are named, and backend declarations confirm it since each backend marks a separate state.

Do examples and test fixtures end up in the diagram?

No, when real stacks exist. Directories whose path contains a segment like examples, tests, fixtures, e2e, wrappers, or ci are set aside. When the examples are the only root modules, the module they demonstrate becomes the subject of the diagram instead.

Is environment-first or component-first better for a diagram?

Both parse. Environment-first gives one stack per environment directory, which is clear but repeats the shared architecture three times. Component-first with per-environment tfvars gives one shape with the differences annotated on the nodes, which is usually the more readable of the two.

Is there a file limit on a large repository?

Yes, three of them. The sync fetches up to 400 .tf files and 500 blobs in total including tfvars, skips any individual file larger than 400KB, and never descends into a .terraform directory. A repository past those limits is analyzed up to the limit rather than rejected, so a very large monorepo still produces a diagram of what was read.