Terraform Repository Strategies and Monorepo Management

Terraform source code should live in a repository. This is close to a universal truth for source code development in general. The most common kind of repository is a git repository, but other similar technologies exist.

There are two common strategies for organizing Terraform code into repositories. Use a one-to-one mapping between Terraform configuration or root module and git repository. Use a single repository for most or all Terraform configurations. This is known as a monorepo. Understanding how to manage Terraform in a monorepo environment requires clear design decisions around structure, dependencies, access, automation, and secrets.

What Is a Monorepo in Terraform Context

Monorepo is short for monolithic repository. The idea with a monorepo is to store all the source code related to multiple projects, applications, and systems in the same git repository.

In the context of Terraform, this means keeping the source code for multiple Terraform root modules, Terraform configurations in the same repository. You might also keep shared Terraform modules for common infrastructure components in the same repository.

A monorepo for Terraform is a repository with more than one Terraform root module. It often contains tens or even hundreds of Terraform root modules, and multiple teams work concurrently in it.

A famous example of a monorepo is Google. Google keeps much of its codebase in a single repository. At Google’s scale, this type of repository can present performance challenges.

One-to-One Mapping Versus Single Repository

Terraform source code should live in a repository. The most common kind of repository is a git repository.

Two common strategies exist.

  • Use a one-to-one mapping between Terraform configuration or root module and git repository
  • Use a single repository for most or all Terraform configurations. This is known as a monorepo

In this context, a monorepo differs from using multiple repositories in Terraform. Managing Terraform in a monorepo environment offers both benefits and challenges.

Key Design Decisions for a Terraform Monorepo

Managing a monorepo for Terraform requires explicit decisions.

Key design decisions for a monorepo for Terraform include:

  • How should you structure the Terraform code? This involves having a strategy for placing Terraform root modules and shared infrastructure modules in the repo
  • How should you handle dependencies? Dependencies come in four variants: state, providers, modules, and shared infrastructure
  • How should you handle access management? This is especially important when multiple developers work in the same repository. You need to utilize the access management features of your version-control system and set up a golden way of working
  • How should you build reusable automation workflows that benefit all the teams working in the monorepo environment?
  • How should you handle application secrets? You should avoid giving everyone with access to the repository read access to secrets

A significant advantage of a monorepo is that your source code is available for every developer to see, which benefits everyone.

Design Area Decision Point
Code Structure Placement of Terraform root modules and shared infrastructure modules
Dependencies State, providers, modules, shared infrastructure
Access Management Version-control system access controls and golden way of working
Automation Reusable workflows for all teams
Secrets Avoid broad read access to secrets

Repository Structure Options

Repo structures vary in granularity and coupling.

One big deployment
Although this typically is an anti-pattern, migrating one big Terraform deployment to env0 is pretty straight forward — just create a template pointing to your main.tf, and run that in env0. This misses out on a lot of things - for example RBAC will be the same for everyone, since there is only one project.

Why is this an anti-pattern? These repo types often create unnecessary interdependencies. A failure in one module or resource cascades, and may affect other non-dependent resources. For instance if you’re slow to deploy a change, it’s possible that a small PR could be blocked by another larger PR. This happens pretty frequently with Atlantis’ preferred repo model.

Deployment per service, states separated by workspaces

  • You have a separate main.tf for each service
  • You use Terraform workspaces to separate the states for the different stages
  • You use .tfvar files to separate the configuration for the different stages. Optional
  • Example

This is a ‘classic’ env0 structure.

Terminology helps clarify discussion.

  • Service - A subsystem/component of your infrastructure. e.g. frontend, backend, databases. There are many ways to split up a system, this could also be “compute / data / networking”
  • Stage or Environment - A different deployment stage or CI/CD environment of your system - e.g. production, staging, dev, qa. To avoid ambiguity with env0’s environment, we’ll use Stage for the rest of this post
  • Deployment - A single Terraform code root module - a group of TF files that are applied together
  • State - The Terraform state file, which is usually per service and stage
  • main.tf - Terraform Deployment entrypoint, usually named main but doesn’t have to be

Remote Backend Requirement

Remote backend is a must
Regardless of your selection, one part is imperative: you should be using a remote backend in order to store your Terraform state.

This means that:

  • You keep total control of your own state files in a cloud of your choice
  • You have direct access to your state files
  • You own access controls to your state files
  • You’ll retain the option to run your Terraform deployment locally if needed
  • This is mandatory if you are migrating existing states to env0

Each environment, e.g., dev, staging, prod, should have its own folder with a main.tf, unique backend block, e.g., with separate key paths, and isolated state location. You can also use terraform.workspace if needed.

Advantages of a Monorepo

Keeping Terraform code collected delivers operational benefits.

  • You don’t need to hunt down all Terraform configurations with the risk of forgetting a few repos
  • Reusing common code and workflows is easier. Keeping Terraform modules in the same repository as the Terraform configurations that use them simplifies module discovery. There’s no need to hunt down which modules exist and where they can be downloaded
  • Using one or a few repositories for Terraform means a lower administrative burden for your git administrators
  • Keeping all Terraform code collected means you get better visibility into what types of resources you are using, which modules are used the most, what Terraform providers are in use, and more. You can start scraping your repositories for this information and build dashboards to visualize your Terraform estate

Disadvantages and Complexity

One of the biggest disadvantages of using Terraform in a monorepo environment is the complexity of managing repository permissions. When you use multiple repositories, you can easily assign permissions for the teams that own the repositories and allow each team to have full admin permissions for their specific repos.

You can still allow other teams to read all repositories and even contribute changes through structured pull-request workflows.

A few other disadvantages are:

  • Managing module upgrades can be complex if you keep your Terraform modules in the same repositories as the Terraform configurations that use them
Factor Monorepo Multi-Repo
Visibility Centralized view of resources, modules, providers Fragmented across repos
Discovery Easier module discovery within same repo Requires hunting across repos
Admin burden Lower number of repos to manage Higher administrative overhead
Permissions Complex to segment per team Easier per-repo assignment
Blast radius Larger, changes can affect many services Isolated per repository

Versioning Modules Inside a Monorepo

What’s the best way to version and release individual Terraform modules when they all live in one repository?

The best approach is to version each Terraform module independently using Git tags with a directory-based naming convention. For example, tag versions like

network/v1.2.0 ormodules/network/v1.2.0

to clearly associate each tag with its module path. Then use git tag and git diff to track changes per module.

When to Migrate Between Strategies

When should I migrate from a multi-repo setup to a Terraform monorepo and vice-versa?

Switch to a monorepo when teams share modules, follow unified workflows, or need centralized policies. It simplifies coordination and reduces duplication. Use multi-repos when teams need independence, deploy separately, or want isolated pipelines. It improves focus, reduces blast radius, and scales better with large teams.

While this is the ideal, many teams have already written some Terraform code, and their repo structure is different. Read on to see your options.

Automation, Change Automation and Providers

Because of this, Terraform builds infrastructure as efficiently as possible, and operators get insight into dependencies in their infrastructure.

Change Automation: Complex changesets can be applied to your infrastructure with minimal human interaction. With the previously mentioned execution plan and resource graph, you know exactly what Terraform will change and in what order, avoiding many possible human errors.

For more information, refer to the What is Terraform? page on the Terraform website.

Documentation is available on the Terraform website.

If you're new to Terraform and want to get started creating infrastructure, please check out our Getting Started guides on HashiCorp's learning platform. There are also additional guides to continue your learning.

This repository contains only Terraform core, which includes the command line interface and the main graph engine. Providers are implemented as plugins, and Terraform can automatically download providers that are published on the Terraform Registry. HashiCorp develops some providers, and others are developed by other organizations. For more information, refer to Plugin development.

To learn more about compiling Terraform and contributing suggested changes, refer to the contributing guide.

To learn more about how we handle bug reports, refer to the bug triage guide.

To learn how to contribute to the Terraform documentation, refer to the Web Unified Docs repository.

Read the documentation for more information on configuring private workers.

Conclusion

A Terraform repository strategy is not a cosmetic choice. It shapes how teams discover modules, share state, enforce policy, and manage blast radius across environments. A monorepo centralizes visibility and simplifies reuse of common code and workflows, which benefits organizations that need unified governance and shared automation. The cost is permission complexity and upgrade coordination when modules and consumers live side by side.

The remote backend remains a non-negotiable foundation regardless of repo shape. State isolation per service and stage, unique backend keys, and workspace separation preserve safety while a monorepo preserves discoverability. Versioning modules independently with directory-based Git tags preserves release autonomy inside a single code base.

Choosing between one-to-one repo mapping and a monorepo is a trade-off between independence and coordination. Multi-repo improves focus, reduces blast radius, and scales better with large teams that need isolated pipelines. Monorepo simplifies coordination and reduces duplication when teams share modules, follow unified workflows, or need centralized policies. The decision should be revisited as team topology, deployment frequency, and compliance requirements evolve, with the repository structure serving the operational model rather than dictating it.

Sources

  1. Spacelift Terraform Monorepo Blog
  2. Env0 TF Repository Strategies and Structures
  3. HashiCorp Terraform GitHub

Related Posts