AnsibleXXXI · Serial Execution and Failure ToleranceFailure Tolerance
Ending a batch or a play deliberately
What you'll learn
- Choose the correct meta verb for a host, a batch, a play or a role
- Predict which hosts continue after each of end_batch, end_host and end_play
- Explain why a meta stop exits zero and what to do about it
- State what clear_host_errors restores and why that is dangerous
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
The failure keywords stop a run when Ansible notices something wrong. Sometimes you notice something wrong — a precondition that is not met, a state that means proceeding is pointless, a maintenance window that has closed — and there is no failure for a keyword to react to.
ansible.builtin.meta provides four verbs for stopping on purpose, each
with a different reach.
| Verb | Stops | Everything else |
|---|---|---|
end_host | this host, for the rest of the play | continues |
end_batch | the current batch | later batches still run |
end_play | the whole play, all batches | later plays still run |
end_role | the current role | the rest of the play continues |
end_batch and end_role are the two that need a version note:
end_batch requires ansible-core 2.12 or later, and end_role 2.18 or
later. This course targets 2.21, so both are available.
The reach of each verb, observed
One playbook, six hosts, serial: 2, with the stop conditioned on
h03 — which is the first host of the second batch. Only the verb
changes.
- name: Which hosts survive the stop
hosts: batchlab
gather_facts: false
serial: 2
tasks:
- name: Stop deliberately
ansible.builtin.meta: end_batch
when: inventory_hostname == 'h03'
- name: After
ansible.builtin.debug:
msg: 'AFTER {{ inventory_hostname }}'$ ansible-playbook -i inv6.ini meta.ymlmeta: end_batch AFTER reached by: h01 h02 h05 h06
meta: end_play AFTER reached by: h01 h02
meta: end_host AFTER reached by: h01 h02 h04 h05 h06Read each line against the batch boundaries:
end_batch ended batch two. h04 — the innocent batch-mate — was
stopped as well, and then batch three ran normally. This is the verb
whose name is most likely to mislead: it does not stop the rollout, it
skips the rest of this batch and carries on.
end_play ended everything. h04 stopped, and batches three
onwards never ran at all.
end_host stopped only h03. h04 continued to the end of the
play, and every later batch ran. This is the surgical one.
Where each one belongs
end_host — this host is not a candidate.
The common case: a host that does not need the change, or cannot take it, and where that is a normal condition rather than an error.
- name: Read the currently deployed version
ansible.builtin.slurp:
src: /etc/app/deployed-version
register: deployed
failed_when: false
- name: Nothing to do on this host
ansible.builtin.debug:
msg: 'Already at {{ release_version }}, leaving the play'
when: (deployed.content | default('') | b64decode | trim) == release_version
- name: Leave the play for hosts already at the target version
ansible.builtin.meta: end_host
when: (deployed.content | default('') | b64decode | trim) == release_versionThe alternative — putting the same when: on all fifteen subsequent
tasks — is what end_host exists to replace. It is also strictly worse,
because the fifteenth when: is the one somebody will forget.
end_play — the run should not continue.
A precondition that applies to the whole change. The maintenance window closed. A dependency is unavailable. A canary batch reported something that means the remaining batches must not proceed.
- name: Stop the rollout if the maintenance window has closed
ansible.builtin.debug:
msg: >-
Maintenance window closed at {{ window_end }}. Stopping before
batch {{ ansible_play_batch | join(', ') }}. Hosts not yet
deployed remain on the previous version.
run_once: true
when: (ansible_date_time.iso8601 > window_end) | default(false)
- name: End the play at the window boundary
ansible.builtin.meta: end_play
when: (ansible_date_time.iso8601 > window_end) | default(false)Because a batched play re-executes per batch, a check like this at the top of the task list runs before every batch — so it stops the rollout at a batch boundary rather than mid-batch, which is the cheap place to stop.
ansible_date_time comes from fact gathering, so this needs
gather_facts: true or an explicit setup task.
end_role — this role has nothing further to do.
Available from 2.18. It ends the current role for the host and continues with the rest of the play. Useful in a role that establishes early that it is not applicable, without the role having to guard every task.
The problem with all of them
A play ended with meta is a successful run.
$ ansible-playbook -i inv6.ini endplay.yml; echo EXIT=$?PLAY RECAP *********************************************************************
h01 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
h02 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
EXIT=0Nothing in that output says the rollout stopped early. failed=0, exit
0, and four of the six hosts have no recap line at all — not even
h03, the host that triggered the stop, or h04 beside it. The recap
reports hosts that completed work, so a run that stopped after batch one
is textually identical to a successful run against a two-host fleet.
clear_host_errors
The counterpart verb: instead of removing hosts, it puts them back.
Upstream names it as the way to reactivate hosts that were marked unreachable — it restores failed and unreachable hosts to the active list so that subsequent tasks run against them again.
There is one defensible use. A play that deliberately disconnects hosts
— a network reconfiguration, a reboot performed outside the reboot
module — expects hosts to drop out and expects them back. Clearing the
errors after they return is repair.
Knowledge check
Knowledge check · 4 questions
Q1. A play with serial: 2 over six hosts hits meta: end_batch on h03, the first host of batch two. Which hosts run the tasks that follow?
Q2. Which meta verb stops one host while letting its batch-mates and all later batches continue?
Q3. A rolling play ends early via meta: end_play. What is true of the run? Select all that apply.
Q4. Adding meta: clear_host_errors after a batch of failures is a reasonable way to let a rolling deploy finish covering the fleet.
Passing score: 75%. Answers are checked in this browser.