Skip to content
Infrastructure as Code iac modules 4 min read

The Module Registry

Writing every module from scratch is wasteful when battle-tested implementations already exist. The Terraform Registry is the official catalog of reusable modules and providers, hosting thousands of community-maintained building blocks for AWS, Azure, GCP, Kubernetes, and more. Pulling a well-maintained module from the registry lets you stand up production-grade infrastructure in minutes instead of reinventing networking, IAM, or autoscaling logic yourself. This page explains how the public registry works, the source and version syntax it expects, how to judge module quality, and how private registries fit into an enterprise workflow.

The public Terraform Registry

The public registry lives at registry.terraform.io and indexes modules published from public GitHub repositories that follow the terraform-<PROVIDER>-<NAME> naming convention. Each module gets a page with rendered documentation, input/output references, example usage, and the underlying source link.

A subset of modules carry a verified badge. These are published by partners whose repositories Terraform has reviewed for ownership and active maintenance — they are not a guarantee of security or correctness, only of provenance. The terraform-aws-modules namespace, maintained by the Anthos/Terraform AWS community, is the most widely used source of verified AWS modules.

OpenTofu maintains its own compatible registry at search.opentofu.org, and because module source addresses are resolved through the same protocol, the vast majority of public Terraform modules work unchanged with OpenTofu.

Source and version syntax

Registry modules are referenced with a three-part address — <NAMESPACE>/<NAME>/<PROVIDER> — in the source argument, paired with a version constraint. The version argument is only valid for registry sources; Git or local sources pin versions differently.

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 5.8.0"

  name = "prod-vpc"
  cidr = "10.0.0.0/16"

  azs             = ["us-east-1a", "us-east-1b", "us-east-1c"]
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]

  enable_nat_gateway = true
  single_nat_gateway = true

  tags = {
    Environment = "prod"
    ManagedBy   = "terraform"
  }
}

output "vpc_id" {
  value = module.vpc.vpc_id
}

Running terraform init downloads the module into .terraform/modules/ and records the resolved version.

Output:

Initializing modules...
Downloading registry.terraform.io/terraform-aws-modules/vpc/aws 5.8.1 for vpc...
- vpc in .terraform/modules/vpc

Terraform has been successfully initialized!

The version argument accepts the standard constraint operators. Pinning matters: a loose constraint can silently pull a breaking change on the next init -upgrade.

ConstraintMeaningAllows
= 5.8.1Exact versiononly 5.8.1
>= 5.8.0Minimum version5.8.0 and any later
~> 5.8.0Pessimistic, patch-only>= 5.8.0, < 5.9.0
~> 5.8Pessimistic, minor-level>= 5.8.0, < 6.0.0
>= 5.0, < 6.0Rangeeverything inside the range

Always set an explicit version for registry modules. Without one, Terraform installs the latest release at init time, making your builds non-reproducible across machines and over time.

Evaluating module quality

A module in the registry is only as good as its maintenance. Before adopting one, weigh it against concrete signals rather than download count alone.

  • Release cadence and recency — look for recent tagged releases and a CHANGELOG. A module last touched two years ago likely lags behind current provider versions.
  • Provider version requirements — check versions.tf/required_providers. A module capped at an old AWS provider can block your upgrades.
  • Open issues and PR responsiveness — a backlog of unanswered security issues is a red flag.
  • Examples and tests — the best modules ship an examples/ directory and automated tests (Terratest, native terraform test).
  • Surface area you actually need — large “kitchen-sink” modules expose hundreds of variables. Audit whether the abstraction fits, or whether you’d be fighting it.
  • License — confirm the license (most are MPL-2.0 or Apache-2.0) is compatible with your usage.

Treat third-party modules as supply-chain dependencies. Pin versions, review the source before first use, and where compliance demands it, vendor a fork into your own private registry rather than pulling from the public internet on every CI run.

Private registries

Organizations rarely want to publish internal modules publicly. A private registry provides the same <NAMESPACE>/<NAME>/<PROVIDER> addressing and version resolution for modules you control. The two common backends are HCP Terraform (formerly Terraform Cloud) and a self-hosted registry that implements the module registry protocol.

Private registry sources are prefixed with the registry host:

module "networking" {
  source  = "app.terraform.io/my-org/networking/aws"
  version = "~> 2.1"

  environment = "staging"
  cidr_block  = "10.20.0.0/16"
}

Authentication is handled through a credentials block, typically populated by terraform login rather than committed to source control.

# ~/.terraform.d/credentials.tfrc.json
{
  "credentials": {
    "app.terraform.io": {
      "token": "REDACTED-team-token"
    }
  }
}

Publishing to a private registry is git-tag driven: connect the VCS repository, then each semver tag (v2.1.0) becomes a selectable version. This gives internal modules the same disciplined, immutable versioning that public ones enjoy.

Best practices

  • Pin every registry module with a pessimistic constraint (~> X.Y) and bump it deliberately, reviewing the CHANGELOG each time.
  • Vet module quality on maintenance signals — recent releases, provider compatibility, tests, and issue responsiveness — not popularity alone.
  • Prefer verified or namespace-trusted modules (e.g. terraform-aws-modules) for foundational infrastructure like VPCs and IAM.
  • Commit your lockfile and module records so terraform init resolves identically in CI and on every developer machine.
  • Stand up a private registry for shared internal modules instead of pasting Git URLs, gaining versioning and access control.
  • Audit the source of any third-party module before first use, and consider vendoring forks where regulatory or supply-chain requirements apply.
Last updated June 14, 2026
Was this helpful?