Skip to main content
RunBook Academy

AnsibleIII · Installing and Designing the ControllerController design

Upgrading Ansible without surprising the fleet

Intermediate⏱ ~22 minbashpython3

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

Not yet marked complete on this device.

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

  1. 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.
  2. 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.
  3. Establish the oldest Python on any managed node, and compare it with the new version managed-node floor.
  4. Install the candidate alongside the current version. Do not replace anything yet.
  5. Syntax-check every playbook in the repository against both versions and diff the results.
  6. Run the estate in check mode against both versions, on staging first, and diff the recaps.
  7. Switch the default on one controller. Leave the old version installed.
  8. 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.

Configuration changea second virtualenv, at a version-named path
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.22
Configuration changeor with pipx, using a command suffix
pipx install --suffix=-next 'ansible-core==2.22.0'
ansible-next --version

Naming 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.

Read-only / Safeevery playbook, both versions, one diff
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.txt

An 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.

Read-only / Safewhat this play would do, before it does anything
$ ansible-playbook --list-tasks playbooks/webtier.yml
playbook: 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.

Configuration changea check-mode rehearsal against staging, on both versions
/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.txt

Deprecation 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.

Configuration changeharvest what the current version is already telling you
ansible-playbook -i inventory/staging --check playbooks/site.yml 2>&1 \
| grep -i 'DEPRECATION\|will be removed'

Knowledge check

Knowledge check · 4 questions

  1. Q1. You are upgrading a controller from ansible-core 2.19 to 2.21. Which porting guides must you read?

  2. Q2. Which of these change categories can alter fleet behaviour WITHOUT producing an error or a warning? Select all that apply.

  3. Q3. A clean `--check` run against staging on the candidate version proves the upgrade is safe for production.

  4. 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.