AnsibleXXXVIII · Git Workflow and CI for AnsibleAutomation as production code
The complete pipeline
What you'll learn
- Order the pipeline stages so each one catches a class the previous cannot
- Annotate every gate with the production failure it prevents, and drop the ones with no answer
- Decide deliberately which gates block a merge and which only report
- Design a degradation path so an urgent fix is not blocked by a slow or broken stage
Prerequisites
Verified against ansible-core 2.21.x · ansible (community package) 14.x · Python (controller) 3.12+ · ansible-lint 26.x · Molecule 26.x · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL / Rocky / AlmaLinux 9.x · 2026-08-11
Six lessons have introduced gates one at a time. This one puts them in order and makes the two decisions that turn a list of tools into a pipeline: what each stage is for, and what happens when it fails.
The organising rule is the one this part has used throughout. Every stage must be able to name the class of production failure it prevents. A stage with no answer to that is not a safety measure; it is a tax on every change, and it will eventually be removed by someone who is right to remove it.
The pipeline, annotated
Ordered cheapest first, because a stage that takes two seconds should never wait behind one that takes six minutes.
| # | Stage | Typical time | What it catches that the previous cannot | Production failure it prevents |
|---|---|---|---|---|
| 1 | yamllint | < 5s | Formatting the parser accepts but humans misread | Review noise hiding a scope change |
| 2 | ansible-playbook --syntax-check | < 10s | A playbook that cannot be assembled: bad keyword, missing role, missing import | A run that dies at 02:00 having done half the estate |
| 3 | Secret scan | 10–30s | A credential in the diff or the history | A disclosed credential and a rotation incident |
| 4 | Vault-encryption gate | < 5s | A vault.yml that is plaintext, or carries the wrong vault id | Production secrets readable by everyone with dev credentials |
| 5 | ansible-inventory --graph per environment | < 10s | Broken inventory, unparseable group, wrong vault id | A deploy that fails at variable load against production |
| 6 | ansible-lint --profile production | 30–90s | Semantic defects: non-idempotent commands, latest packages, missing FQCN | A task that reports changed forever, restarting services nightly |
| 7 | Molecule, path-filtered | 2–10 min | Run-time behaviour: does the role work from nothing, and twice | A role that appends a line per run, or fails on a fresh host |
| 8 | --check --diff against staging | 1–5 min | What this change would actually do, on real hosts, with this environment’s variables | A change whose effect nobody could see before it landed |
| 9 | Human review with that evidence | — | Intent, scope, rollback, timing | Correct automation aimed at the wrong machines |
Stage 5 is the one people leave out and it is worth its ten seconds. It is
the only static stage that loads and decrypts group_vars, so it is the
only one that fails on a wrong vault id — --syntax-check exits 0 in that
case, measured on 2.21.3.
Stages 1 to 6 are the ones you can express as a script and run identically on a laptop and in CI, which is what stops a pipeline failure from being a surprise:
#!/usr/bin/env bash
set -euo pipefail
yamllint -s -f parsable .
for env in development staging production; do
ansible-playbook -i "inventories/$env" playbooks/site.yml --syntax-check
done
scripts/scan-secrets.sh
scripts/check-vault-encrypted.sh
for env in development staging production; do
ansible-inventory -i "inventories/$env" --graph > /dev/null
done
ansible-lint --profile productionThe vault ids for stages 2 and 5 come from the runner’s environment; note that the loop deliberately covers every environment, because an inventory that only breaks for production is the one that breaks at the worst moment.
Blocking or reporting
Not every gate should stop a merge, and getting this wrong in either direction is expensive.
Block on: syntax check, secret scan, vault-encryption gate, inventory load, lint at the chosen profile, and Molecule for the roles the change touches. These are all deterministic — they fail because the change is wrong, not because the world moved.
Report on: the check-mode diff. Its job is to produce evidence, and it depends on a staging environment that can be down for reasons unrelated to the change. Blocking on it means a staging outage blocks every merge in the repository, which teaches everyone to use the override. Attach the output, mark the stage failed if it errored, and let the human decide whether they have enough to review.
Report on: anything flaky, until it is not. A gate that fails spuriously once a week trains people to re-run rather than read, and a re-run habit applies equally to the real failures.
Making it fast enough not to be bypassed
The pipeline competes with an alternative that is always available: merging without it. Every minute it costs makes that alternative more attractive, and the moment it wins is an incident.
Four things buy the most time back:
Path filtering. A change under roles/webserver/ runs the webserver
Molecule scenarios and nothing else. This is the single biggest win in the
whole pipeline and it is a few lines of configuration.
Parallel stages. Stages 1 through 6 are independent of one another. Run them concurrently and the static portion costs as long as its slowest member rather than their sum.
Cached dependencies. ansible-galaxy install -r requirements.yml
against a network on every job is both slow and a source of failures
unrelated to the change. Cache it keyed on the file’s hash — which works
correctly precisely because the versions are pinned.
Scheduled full runs. The complete platform matrix, every role, every scenario, nightly against the default branch. It catches the cross-role and upstream-image regressions that path filtering misses, without putting hours into the merge path.
Knowledge check
Knowledge check · 4 questions
Q1. Why does an ansible-inventory --graph stage earn a place alongside --syntax-check, which is faster and already runs?
Q2. Which stages should report rather than block a merge? Select all that apply.
Q3. A pipeline whose stages all pass is sufficient evidence that a change is safe to apply to production.
Q4. The container registry is rate-limiting, Molecule cannot run, and an urgent security fix needs to merge. What is the designed response?
Passing score: 75%. Answers are checked in this browser.