
Terraform modules age like any other dependency: security advisories land, cloud provider APIs shift underneath them, and the version you pinned a year ago becomes the version nobody wants to touch. Dependabot handles this the same way it handles npm or pip dependencies, by watching module and provider version constraints and opening a pull request when a newer release exists. This post covers how the terraform package-ecosystem works, how to configure it, what to do when it stops finding updates, and how to point it at a private module registry once you outgrow the public one.
Dependabot is GitHub's dependency-update bot. Since 2022, it has shipped a terraform package-ecosystem that scans .tf files for module source and version constraints and provider version constraints, checks the relevant registry for newer releases, and opens a pull request bumping the constraint when one exists. It works against modules hosted on the public Terraform Registry and against modules hosted on a publicly reachable Git repository with no extra configuration required.
Private registries are the exception. If your modules live behind authentication, whether that's Scalr, HCP Terraform's private registry, or something self-hosted, Dependabot needs a registries block in dependabot.yml and a token to read from it. The basic setup below covers the public case; the private-registry section further down covers the rest.
For modules on the public registry or a public Git repository, the setup is just a dependabot.yml file:
version: 2
updates:
- package-ecosystem: "terraform"
directory: "/"
schedule:
interval: "weekly".github/dependabot.yml to your repository with the block above.directory if your Terraform code lives in a subdirectory, or add another entry under updates for each additional directory.That's the entire setup for public modules. No token, no registry block, nothing to store as a secret. The next time a module you reference ships a new version, Dependabot opens a pull request with the version bump.
A few options beyond schedule.interval are worth knowing:
version: 2
updates:
- package-ecosystem: "terraform"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
target-branch: "main"
groups:
aws-modules:
patterns:
- "*aws*"
ignore:
- dependency-name: "terraform-aws-modules/vpc/aws"
versions: ["4.x"]
cooldown:
default-days: 3groups combines updates that match a pattern into one pull request instead of one PR per module, which matters once a repo references more than a handful of modules.ignore skips specific modules or version ranges, useful for a module you're deliberately holding back.open-pull-requests-limit caps how many version-update PRs Dependabot keeps open at once (default is 5); set it to 0 to pause version updates for the ecosystem without removing the config.target-branch points Dependabot at a branch other than your default, for PRs.cooldown.default-days delays a newly published version from triggering a PR for a set number of days, though Terraform doesn't support the per-SemVer-level cooldown settings (semver-major-days, etc.) that some other ecosystems get.One option that does not apply here: versioning-strategy. It's supported for ecosystems with a lockfile-driven workflow (npm, bundler, pip, and a handful of others), but Terraform isn't on that list, so setting it has no effect.
Terraform module health is the maintainability and reliability of the infrastructure-as-code components your teams build on. Modules accumulate debt the same way any dependency does, and an outdated one can introduce compatibility problems or security exposure that spreads through everything built on it. Skip the maintenance and the cost tends to show up as an emergency fix during an incident rather than a planned update during a maintenance window.
A versioned module registry, whether that's the public Terraform Registry or a private one, is what makes tracking that debt possible. Once modules carry versions in one place, you can see which workspaces run an outdated version and test an upgrade before shipping it, instead of guessing. Dependabot is what turns that visibility into something that happens automatically instead of during a quarterly audit.
Most problems come down to one of these:
token field in dependabot.yml exactly.groups.patterns if you're using grouping; an overly broad pattern can pull an unrelated module into the same PR.Everything above assumes public modules. Once modules move behind authentication, whether in Scalr, HCP Terraform's private registry, Artifactory, or a self-hosted option, Dependabot needs a registries block and a token with read access. The shape is the same across providers; the example below walks through it with Scalr's private module registry.
dependabot).environments:read and modules:read. Those two cover every module in the account and environment scopes; nothing broader is needed.
Go to Settings > Secrets and variables > Dependabot in your repository, click "New repository secret," and add SCALR_REGISTRY_TOKEN with the token from Step 1.

version: 2
updates:
- package-ecosystem: "terraform"
directory: "/"
schedule:
interval: "weekly"
registries:
- scalr-private-registry
registries:
scalr-private-registry:
type: "terraform-registry"
url: "https://your-account.scalr.io"
token: "${{secrets.SCALR_REGISTRY_TOKEN}}"The registries block under updates tells Dependabot which registry definition to use; the top-level registries block defines it, including the URL and the token from your GitHub secret.

Commit .github/dependabot.yml, push, and check Pull Requests > Dependabot in GitHub. The next time a module you reference gets a new release in the registry, Dependabot opens a pull request with the version bump:

If you're standardizing on Scalr's registry, its module usage reporting is a useful cross-check on top of Dependabot: it shows which workspaces reference outdated versions, which is the fastest way to spot a repo that skipped this setup entirely.
Dependabot's terraform package-ecosystem covers public modules with a four-line dependabot.yml and no credentials at all. Private registries, Scalr's included, add one registries block and a scoped token on top of that same base config. Either way, the payoff is the same: module updates show up as reviewable pull requests instead of a version check someone has to remember to run.
