AnsibleXLVIII · Maintenance Windows and RollbackMaintenance windows and rollback
The reverse is part of the change
What you'll learn
- Write a reverse procedure specific enough that a second operator could execute it
- Identify the point of no return in a change and state it explicitly in the plan
- Define objective abort criteria rather than criteria that depend on judgement under pressure
- Apply the rule that a change with no written reverse does not get a window
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
Rollback is planned before execution, not improvised after it.
That is not a preference. It follows directly from the previous lesson: if Ansible has no undo, then the reverse of a change is something a person builds, and the two moments available for building it are before the window, when the change is understood and nobody is under pressure, or during the incident, when neither is true.
The industry default is the second one. This lesson is about making the first one non-optional.
The rule
A change with no written reverse does not get a maintenance window.
Not “should have one”. Does not get one. The reverse procedure is an
entry gate, in the same way a peer review or a --syntax-check is an
entry gate, and for the same reason: it catches a class of problem that
is cheap to fix now and expensive to fix later.
The rule has a useful side effect that is arguably worth more than the artefact. Writing the reverse forces someone to think through what the change actually does, and that thinking regularly finds a problem in the forward plan. A change whose reverse cannot be written is usually a change that has not been understood.
What the artefact contains
Six fields. A change plan that has all six is executable by someone who did not write it, which is the whole test.
| Field | The question it answers | Failure when it is missing |
|---|---|---|
| Reverse procedure | Exactly which commands, in order, on which hosts | Improvised at 03:00 from memory |
| Executor | Who is permitted to run it, and who is on call | Nobody feels authorised to pull the trigger |
| Duration | How long it takes end to end | The decision is made without knowing the cost |
| Cost | What the rollback itself breaks or interrupts | The reverse causes a second outage |
| Trigger criteria | The objective conditions that mean roll back | Debate during the incident |
| Point of no return | The step after which forward is cheaper than back | Someone attempts a reverse that no longer exists |
Reverse procedure
Specific to the point of being boring. Not “restore the config” but the actual invocation, with the actual limit, tested against the actual inventory.
# Step 1 - confirm what will be touched, changing nothing.
ansible-playbook -i inventories/prod site.yml \
--limit 'appservers:&batch_a' \
--tags rollback_config \
--check --diff --list-hosts
# Step 2 - restore the previous configuration.
ansible-playbook -i inventories/prod site.yml \
--limit 'appservers:&batch_a' \
--tags rollback_config
# Step 3 - validate. Exit non-zero means the rollback did not land.
ansible-playbook -i inventories/prod validate.yml \
--limit 'appservers:&batch_a'Three properties make that usable under pressure: the commands are copyable, the host selection is identical to the forward change so there is nothing to re-derive, and step 3 tells the operator whether it worked rather than leaving them to decide.
Trigger criteria
The criteria must be objective, because the person applying them will be tired, invested in the change, and subject to the sunk-cost pull of “it is probably nearly working”.
| Written like this | Or like this |
|---|---|
| “if performance degrades” | “if p99 latency exceeds 400 ms for 5 consecutive minutes” |
| “if errors increase” | “if the 5xx rate exceeds 1% of requests” |
| “if the canary looks unhealthy” | “if the canary fails the validation play” |
| “if it takes too long” | “if the window is not complete by 03:30” |
The left column is not a criterion. It is a request for a judgement call under conditions that make judgement calls bad. The right column can be evaluated by someone who arrived ten minutes ago, and — more importantly — can be enforced mechanically, which is what lesson 5 is about.
The clock criterion deserves particular attention. Almost every change plan omits it, and time is the criterion that fires most often. A change that is still going at the end of the window has failed even if nothing is broken, because the next thing to happen is the business day.
Point of no return
Every change plan should name the step after which rolling back costs more than going forward. This single line prevents more damage than the rest of the document.
Consider a deployment with a forward-only schema migration:
| Step | Reverse available | Cost of reversing |
|---|---|---|
| 1. Deploy new artefact to canary | Yes | Swap the symlink back, 30 s |
| 2. Validate canary | Yes | Swap the symlink back, 30 s |
| 3. Deploy to remaining batches | Yes | Swap symlinks back, 4 min |
| 4. Run the schema migration | Point of no return | Restore the database, 40 min, data loss |
| 5. Enable the new feature flag | Forward only | Fix forward |
Before step 4 the reverse is a symlink swap. After step 4 the reverse is a database restore with an RPO measured in minutes of lost writes. Those are not the same decision and the plan must not present them as one line called “rollback”.
Where the artefact lives
In the repository, next to the change, reviewed with the change. Not in a wiki page, not in a ticket comment, not in the head of the person who wrote the role.
changes/
2026-08-14-app-2.5.0/
plan.md # forward steps, timings, approvers
reverse.md # the six fields above
preflight.yml # asserts run before the window opens
validate.yml # asserts run after each batch
Keeping it in the repository buys three things: it is reviewed by the same process as the code, it is versioned alongside the revision it reverses, and it is available to someone who was not in the planning meeting. That last one is the point — the artefact exists for the person who did not write the change.
Knowledge check
Knowledge check · 4 questions
Q1. A deployment plan lists five steps, with a forward-only schema migration at step 4. At step 5 the application misbehaves and an operator runs the documented rollback, which swaps the release symlink back. What is the likely outcome?
Q2. A rehearsal of a reverse procedure in pre-production catches which of these failures? Select all that apply.
Q3. A change with no possible reverse may still be scheduled, provided the plan states that plainly and moves the mitigation to a rehearsed restore with its own RTO.
Q4. Why are rollback tasks given the never tag rather than being kept in a separate playbook?
Passing score: 75%. Answers are checked in this browser.