AnsibleIII · Installing and Designing the ControllerController design
Upgrading Ansible without surprising the fleet
What you'll learn
- Treat a controller upgrade as a change whose blast radius is the whole fleet
- Use the porting guide and changelog to enumerate what a version bump removes
- Install a candidate version alongside the current one and prove the estate against both
- Read deprecation warnings as the early-warning system for the upgrade after next
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
Upgrading Ansible feels like maintenance. It is not. It is a change to the tool that executes every task against every host you own, made without touching a single playbook, and its blast radius is the whole fleet.
Treat it accordingly: the same enumeration, the same rehearsal, the same staged rollout you would give a change to the fleet itself. The procedure below is that treatment, and it costs an afternoon.
What actually changes across a minor release
Four categories, in rough order of how much trouble each causes.
Removed features. ansible-core deprecates and then removes on a
published schedule. A removal is the only category that fails cleanly:
the run stops with an error naming the thing. Unpleasant, but honest.
Changed defaults. The worst category. A default that moves changes behaviour silently — no error, no warning, a different outcome on the fleet. These are exactly what the porting guide exists to enumerate.
Stricter validation. Something that previously produced a warning now fails, or something loose in your YAML is now rejected. Fails cleanly, but often in a place unrelated to what you were changing.
New deprecations. Nothing breaks today. These are the warnings that tell you what the next upgrade will remove, which is why the estates that suppress warnings are the estates for which every upgrade is a crisis.
The managed-node Python floor is a fifth thing, easy to miss because it is not a code change at all. Moving from core 2.19 to 2.21 raises the supported managed-node Python from 3.8 to 3.9. No playbook changed; some hosts left the supported set.
The procedure
- Read the porting guide for every version you are crossing, not only the target. Going 2.19 to 2.21 means reading both the 2.20 and the 2.21 guides.
- Collect the current deprecation warnings from a full check-mode run. These name what the next release removes, and they are the cheapest predictor you have.
- Establish the oldest Python on any managed node, and compare it with the new version managed-node floor.
- Install the candidate alongside the current version. Do not replace anything yet.
- Syntax-check every playbook in the repository against both versions and diff the results.
- Run the estate in check mode against both versions, on staging first, and diff the recaps.
- Switch the default on one controller. Leave the old version installed.
- Run production change on the new version for a full cycle before removing the old one.
Steps four through six are what make this an upgrade rather than a gamble, so they get the rest of the lesson.
Install alongside
Both install methods from earlier in this part support this directly, which is one of the reasons the course prefers them.
python3.12 -m venv /opt/estate/venv-2.22
/opt/estate/venv-2.22/bin/pip install 'ansible-core==2.22.0'
/opt/estate/venv-2.22/bin/ansible-galaxy collection install \
-r /opt/estate/requirements.yml -p /opt/estate/collections-2.22pipx install --suffix=-next 'ansible-core==2.22.0'
ansible-next --versionNaming the path after the version rather than after its role — venv-2.22,
not venv-new — matters more than it looks. venv-new is accurate for
about a month and misleading for years afterwards, and the person
debugging a scheduled job at 3 a.m. has no way to tell what is in it.
Prove against both
Two levels of proof, and neither connects to a managed host.
Syntax and structure. --syntax-check parses the playbook, resolves
imports and includes, and validates task structure. It catches removed
keywords, renamed parameters and anything the new parser rejects.
cd /opt/estate
for p in playbooks/*.yml; do
echo "== $p"
/opt/estate/venv/bin/ansible-playbook --syntax-check "$p" 2>&1
done > /tmp/syntax-current.txt
for p in playbooks/*.yml; do
echo "== $p"
/opt/estate/venv-2.22/bin/ansible-playbook --syntax-check "$p" 2>&1
done > /tmp/syntax-candidate.txt
diff /tmp/syntax-current.txt /tmp/syntax-candidate.txtAn empty diff is the outcome you want. Anything else is a list of work to do before the upgrade, produced in a couple of minutes, with no risk to anything.
Targeting. --list-hosts and --list-tasks tell you whether the
new version resolves your inventory and your conditionals to the same
set of hosts and the same set of tasks. Neither connects.
$ ansible-playbook --list-tasks playbooks/webtier.ymlplaybook: playbooks/webtier.yml
play #1 (webservers): Configure the web tier TAGS: []
tasks:
Install packages TAGS: [packages]
Deploy configuration TAGS: [config]
Ensure service is running TAGS: [service]Illustrative output
Behaviour. Check mode is the closest you get to a rehearsal, and it is the first step in this procedure that touches the fleet at all — it connects, gathers facts, and asks each module what it would change.
/opt/estate/venv/bin/ansible-playbook -i inventory/staging \
--check --diff playbooks/site.yml > /tmp/check-current.txt 2>&1
/opt/estate/venv-2.22/bin/ansible-playbook -i inventory/staging \
--check --diff playbooks/site.yml > /tmp/check-candidate.txt 2>&1
diff /tmp/check-current.txt /tmp/check-candidate.txtDeprecation warnings are the early-warning system
Every estate that finds upgrades traumatic has the same habit: warnings are noise, so warnings are suppressed.
A deprecation warning names something that will stop working in a specific future release. It arrives one or two releases before the removal, which is deliberately enough time to fix it during ordinary work rather than during an upgrade. Suppressing them does not remove the work; it defers all of it into a single day, later, under pressure.
ansible-playbook -i inventory/staging --check playbooks/site.yml 2>&1 \
| grep -i 'DEPRECATION\|will be removed'Knowledge check
Knowledge check · 4 questions
Q1. You are upgrading a controller from ansible-core 2.19 to 2.21. Which porting guides must you read?
Q2. Which of these change categories can alter fleet behaviour WITHOUT producing an error or a warning? Select all that apply.
Q3. A clean `--check` run against staging on the candidate version proves the upgrade is safe for production.
Q4. Why does this course insist on installing the candidate version alongside the current one rather than upgrading in place?
Passing score: 75%. Answers are checked in this browser.