Publish the change as a pre-release version, such as 2.0.0-beta.1, and have the trial caller name that exact version. Terraform does not match pre-release versions through range operators, so no other caller receives it by accident. That makes pre-releases a safe way to test a breaking module change.
What Terraform does
HashiCorp's version constraints documentation says a pre-release version has a dash introduced suffix, such as 1.2.0-beta. You select one by setting the exact version with =, or by giving the exact version with no operator. It also states that Terraform does not match pre-release versions on the >, >=, <, <= or ~> operators.
So a caller using ~> 1.2 or >= 1.0 will not be moved onto 2.0.0-beta.1. Only a caller who writes the exact version will get it.
Why this suits shared modules
A shared module with many callers cannot test a breaking change on all of them at once. A pre-release lets one willing team try it first, with real configuration, while everyone else stays put. If it goes wrong, the trial team changes one line back.
Registry tags also need to be valid. For the public registry, HashiCorp requires release tags to be semantic versions with an optional v prefix. See versioning a shared module with semver.
A trial plan
- Merge the change to a branch through review, with owners approving.
- Tag a pre-release on that commit, for example 2.0.0-beta.1.
- Write release notes listing each breaking change and the edit needed.
- Pick one or two caller stacks that are not production critical.
- In each, set the module version to the exact pre-release and run
terraform init. Changing a registry version requires rerunning init. - Read the plan closely. Any replacement of existing objects needs a decision before release.
- Fix issues and tag the next pre-release, then repeat.
- When trials are clean, tag the final release and announce it.
Table: who gets what
| Caller constraint | Receives 2.0.0-beta.1? | Receives 1.9.0? |
|---|---|---|
| Exact 2.0.0-beta.1 | Yes | No |
~> 1.8 | No | Yes |
>= 1.0 | No | Yes |
| Git ref to a branch | Depends on the branch | Depends on the branch |
The last row is the reason to prefer tagged versions over branch refs: a branch ref moves with every commit. See pinning module sources.
Cautions
- Do not leave production callers on a pre-release. Treat it as temporary, with an end date.
- Protect pre-release tags the same as final ones. See protecting release tags with a ruleset.
- Pre-releases are not security tested by default. A beta that touches IAM or networking deserves the same review as a final release.
- Update bots may or may not propose pre-releases. See whether update bots should propose pre-release versions.
After the trial
Move trial callers to the final version promptly, and remove any exact pre-release pins. A stale pin to a beta is easy to forget and hard to notice. A repository search for the suffix, run during review, catches strays.
Where Safeguard fits
Safeguard's SBOM and dependency scanning can inventory what your repositories declare, which helps find forgotten pins, and policies can gate pull requests that change them.
Quick check
Will a tilde arrow range pick up a beta module?
No. Terraform does not match pre-release versions on that operator or the other range operators.
How does a caller opt in to a pre-release?
By setting the exact version, with or without the equals sign, then rerunning terraform init.
Sources
Last reviewed: 19 October 2026.