Use the strongest reference each tool supports for production, which is usually a commit hash or a digest, and keep ranges for lower environments. Each tool names this differently, so the table below maps them. The idea is the same everywhere: production should only change when a person approves a reviewed edit.
The three strengths
A tag is a name that can be moved. A semver range is a rule that selects the newest matching version. A commit hash or digest identifies exactly one thing. Moving down that list reduces surprise and increases the work of updating.
What each tool supports
| Tool | Reference options in its documentation | Strongest option |
|---|---|---|
| Terraform Git module | Default branch, or ref with branch, tag or SHA | ref with a full SHA |
| Terraform registry module | version constraint | Exact version |
| Terragrunt unit | Same source syntax, ref parameter | ref with a SHA |
| kustomize remote base | ref with branch, tag or full hash | Full commit hash |
| Argo CD | Branch, HEAD, tag, semver range, commit SHA | Commit SHA |
| Flux OCIRepository | tag, semver, digest | digest, plus signature verification |
| Crossplane provider | Tag or digest in the package field | digest |
| Kubernetes image | Tag, digest, or both | digest |
| Packer plugin | Version constraint | Exact version |
| TFLint plugin | Exact version only | Exact version |
Notes from the documentation behind that table: Argo CD calls commit pinning the most restrictive option, typically used for production. Flux says a digest takes precedence over all other reference fields. Crossplane says digests give deterministic, repeatable installs. Kubernetes says digests are immutable while tags can move. TFLint does not support version ranges for plugins.
Choosing by environment
- Development: branches or ranges are fine. You want fast feedback and low stakes.
- Staging: narrow ranges or tags. New versions land here first and get tested.
- Production: commit hash or digest, changed only by pull request, reviewed by someone other than the author.
Promote the identical reference from staging to production when you can. A digest tested in staging is the same digest running in production.
Costs of strong pinning
Pins age. A hash that never changes also never gets a fix. Plan for it:
- Use an update bot or scheduled job that opens pull requests for new versions.
- Pair every bump with a scan result, so reviewers see what changes in terms of known issues.
- Set a maximum age for pins on critical components and alert when one is exceeded.
- Make rollback a one line change, because the previous reference is still in version control.
Strong pins without update discipline trade one risk, surprise change, for another, stale components.
Protecting tags if you must use them
Where a tool only supports tags in practice, protect them. Restrict who can delete or force-move tags in the source repository, require signed tags if your hosting supports them, and mirror the repository so you control it. For OCI sources, use signature verification such as the Cosign and Notation support described in the Flux documentation.
Quick audit
Run these searches across your repositories this week: Git module sources without ref, remote kustomize URLs without a full hash, Argo CD applications tracking HEAD, images without digests, and plugin blocks without exact versions. Each hit is a candidate for pinning.
Where a scanner fits
Pinned references tell you what you run, and a scanner tells you whether it is known to be vulnerable. Safeguard's SBOM and dependency scanning, continuous monitoring and policy gates on pull requests fit this loop. Confirm your scanner reads each of the file types above. Free scan: gold.safeguard.sh/scan.
Try it yourself
Click through it at your own pace, or open it full screen.
See how a pinned component still gets re-checked over time, which is how strong pins avoid going stale.
Quick check
Is a semver range ever acceptable in production?
It can be, if you accept automatic updates within the range and have tests and monitoring. Most teams prefer exact references in production and ranges in staging.
Does pinning replace scanning?
No. Pinning makes a version stable. Scanning tells you whether that stable version has known problems.
Sources
- Tracking and Deployment Strategies, Argo CD documentation
- OCI Repositories, Flux documentation
- Providers, Crossplane documentation
- Images, Kubernetes documentation
- Use modules in your configuration, HashiCorp
- Units, Terragrunt documentation
- Remote targets, kustomize documentation
- Configuring Plugins, TFLint documentation
Last reviewed: 19 October 2026.