Skip to main content
RunBook Academy

Git, CI/CD & GitOpsLXVII · Dependency PinningConfigPinning

Ansible collection and role pinning — requirements.yml with version constraints

Advanced⏱ ~24 mingit

What you'll learn

  • Pin Ansible collections by exact version in `requirements.yml` and install with `ansible-galaxy collection install -r`
  • Pin Ansible roles by source, scm, version, and name; prefer vendored roles for security-critical playbooks
  • Resolve a collection or role to a content address and verify the source
  • Establish a re-pin workflow that treats collection upgrades as supply-chain changes

Prerequisites

Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x

Not yet marked complete on this device.

Ansible collections and roles are pulled from Galaxy, Git, or a private automation hub at runtime. The requirements.yml file declares the consumer intent — which collections and roles the playbook needs, in what version range. The resolution is performed by ansible-galaxy against the registry; the registry can publish a new satisfying version between two consecutive runs, and the run will pick up the new version without any change to the playbook. The discipline of pinning binds every collection and every role to a content address and reviews the upgrade.

Pin collections by exact version in requirements.yml

A collection pin takes the form namespace.collection:version. An exact pin freezes the version; a range narrows the selection but leaves drift open.

# collections/requirements.yml
collections:
  - name: community.general
    version: "8.3.0"
  - name: amazon.aws
    version: "7.2.0"
  - name: kubernetes.core
    version: "3.0.0"

The version: "8.3.0" is an exact pin. The ansible-galaxy collection install -r collections/requirements.yml command resolves each collection to the pinned version and downloads the artifact from Galaxy. A playbook that pins version: ">= 8.0" instead is a playbook whose bytes are chosen by Galaxy at install time.

flowchart LR
    A["Range constraint"] --> B["Galaxy resolves"]
    B["Galaxy resolves"] -. "new version" .-> C["Different bytes today"]
    D["Exact version"] --> E["Artifact v8.3.0"]
    E["Artifact v8.3.0"] --> F["Same bytes forever"]

The Galaxy artifact is a tarball served from galaxy.ansible.com or from a private automation hub. The tarball has a SHA-256 checksum published alongside it; the checksum is the content address. The exact version pin freezes the tarball; the checksum verification confirms the bytes.

Pin roles by source, scm, version, and name

Roles can be pulled from Galaxy (the same registry as collections) or directly from a Git repository. A role pin that uses scm: git must also pin version: to a branch, a tag, or a commit SHA.

# roles/requirements.yml
roles:
  - name: geerlingguy.docker
    version: "3.1.1"
  - name: my-org.internal-role
    src: https://github.com/my-org/ansible-role-internal.git
    scm: git
    version: 7d4f2a1c9b8e6f3a2c1d4e5f6a8b9c0d1e2f3a4b

The Galaxy role pin (geerlingguy.docker) freezes the artifact version. The Git role pin (my-org.internal-role) freezes the commit SHA; the SHA is the content address and cannot be moved without rewriting history. A Git role pin that uses version: main is a role pin whose bytes are chosen by the upstream at install time.

Resolution and verification

The ansible-galaxy resolution is the moment the registry is consulted. The production discipline is to resolve in a controlled step (CI, an isolated runner, a deliberate operator action), record the resolved artifact, and ship the artifact to the runtime without re-resolution.

# CI: resolve against Galaxy, install into the local collection path
ansible-galaxy collection install -r collections/requirements.yml -p ./collections
# CI: resolve Git roles, install into the local role path
ansible-galaxy role install -r roles/requirements.yml -p ./roles

The CI step records the resolved versions and the checksums. The runtime uses the local collections/ and roles/ paths; the runtime does not contact Galaxy. The registry is consulted once, under review, and the result is vendored into the build.

# Runtime: use the local vendored collections and roles
ansible-playbook site.yml \
  --collections-path ./collections \
  --roles-path ./roles

Re-pinning as a deliberate upgrade

A collection or role upgrade is a supply-chain change. The workflow is:

  1. Identify the upgrade. Read the upstream release notes and advisories.
  2. Resolve the new version. Update requirements.yml with the new exact version or the new commit SHA.
  3. Run ansible-galaxy install to verify the new artifact downloads and matches the checksum. Fail the PR if the checksum does not match.
  4. Run the playbook’s test suite (molecule or equivalent) against the new artifact. The suite is the team’s evidence that the upgrade does not break the playbook.
  5. Commit the updated requirements.yml in a pull request. The PR contains the new version, the resolved checksum, the upstream release notes, and the test results.

A Renovate-equivalent bot can automate step 2 and open the PR in step 5; the team still performs steps 3 and 4.

Production discipline

  1. Pin every collection by exact version. No >= or ~> for security-critical playbooks.
  2. Pin every Git role by commit SHA. No main or master refs.
  3. Verify the checksum at install time. A download without a checksum verification is a supply-chain hole.
  4. Vendor security-critical collections and roles. The registry is consulted once, under review; the runtime executes the vendored copy.
  5. Re-pin deliberately. A collection or role bump is a supply-chain change.

Cross-course references

  • Git, CI/CD & GitOps — Part LXVII-01 (Pinning Discipline) defines the discipline that motivates collection pinning.
  • Git, CI/CD & GitOps — Part LI-05 (Molecule) covers the integration tests that validate a collection or role upgrade.
  • Ansible for Production Sysadmins — Part XXXVII (RepoArch) covers the repository layout that hosts vendored collections and roles.
  • Linux for Production Sysadmins — Part XXXIV (ConfigMgmt) covers the configuration-management analogue at the OS level.

Quiz

Knowledge check · 4 questions

  1. Q1. Why does production-grade Ansible pinning reference a collection by exact version (e.g., `version: "8.3.0"`) rather than by range (e.g., `version: ">= 8.0"`)?

  2. Q2. A Git role pinned by `version: main` is a role whose bytes are chosen by the upstream at install time.

  3. Q3. Explain what vendoring a collection or role closes that an exact version pin leaves open.

  4. Q4. Identify the gap in the team's collection-pinning practice and the rule that closes it.

    Team T maintains 30 playbooks that target production Linux hosts. The playbooks use `community.general`, `amazon.aws`, and `kubernetes.core`. Each playbook's `collections/requirements.yml` uses range constraints (`version: ">= 7.0"`). The CI pipeline installs collections with `ansible-galaxy collection install -r` without checksum verification. The runtime hosts pull collections from Galaxy at playbook start, not from a vendored copy. A new minor version of `community.general` (8.4.0) is published to Galaxy; the new version contains a breaking change in a module the team relies on. Every playbook run on the production hosts fails because the module's return type changed.

Passing score: 75%. Answers are checked in this browser.