AnsibleXXXIV · Concurrency, Strategies and PerformanceConcurrency, strategies and performance
linear, free and host_pinned
What you'll learn
- Name the four strategy plugins in ansible.builtin and what each one changes
- Describe the task-level synchronisation barrier that linear provides
- State what free removes and which operational guarantees depend on it
- Decide when free is appropriate and when it is incompatible with the change being made
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 default strategy has a property nobody notices until it is gone: when task 3 finishes, every host has finished task 3. That is a barrier, and a great deal of what this course teaches about staged, verifiable change rests on it.
strategy: free removes it. That is faster, and it is not a free
improvement — it is a trade, and the thing being traded away is the reason
a rolling deployment is meaningful rather than just parallel.
Four, and only four
$ ansible-doc -t strategy -lansible.builtin.debug Executes tasks in interactive debug session
ansible.builtin.free Executes tasks without waiting for all hosts
ansible.builtin.host_pinned Executes tasks on each host without interruptio...
ansible.builtin.linear Executes tasks in a linear fashionThree of them are execution strategies and one is a debugger. strategy is
a Play keyword only — it cannot be set per task or per block — and the
default is linear, which ansible-config dump confirms as
DEFAULT_STRATEGY(default) = linear.
linear: the barrier you are used to
One task at a time, across all hosts in the current batch, and the play
does not advance until every host has finished that task or dropped out.
forks decides how many of those hosts are worked on simultaneously; the
barrier is unaffected by it.
$ ansible-playbook -i strat.ini strat.yml -f 5TASK [Task A (web1 is slow)] ***************************************************
ok: [web2]
ok: [web3]
ok: [web1]
TASK [Task B] ******************************************************************
ok: [web1] => { "msg": "B on web1" }
ok: [web2] => { "msg": "B on web2" }
ok: [web3] => { "msg": "B on web3" }
TASK [Task C] ******************************************************************
ok: [web1] => { "msg": "C on web1" }
ok: [web2] => { "msg": "C on web2" }
ok: [web3] => { "msg": "C on web3" }web2 and web3 finished task A immediately and then waited six seconds
for web1 before anyone started task B. That waiting is the cost, and it
is exactly what free recovers.
Note also that the ok: lines under task A are in completion order —
web2, web3, then web1. linear dispatches in parallel up to forks
and prints results as they arrive; it is the advance to the next task
that is synchronised, not the dispatch.
free: no barrier
$ ansible-playbook -i strat.ini strat.yml -f 5 -e strat=freeTASK [Task A (web1 is slow)] ***************************************************
ok: [web2]
ok: [web3]
TASK [Task B] ******************************************************************
ok: [web2] => { "msg": "B on web2" }
ok: [web3] => { "msg": "B on web3" }
TASK [Task C] ******************************************************************
ok: [web2] => { "msg": "C on web2" }
ok: [web3] => { "msg": "C on web3" }
TASK [Task A (web1 is slow)] ***************************************************
ok: [web1]
TASK [Task B] ******************************************************************
ok: [web1] => { "msg": "B on web1" }
TASK [Task C] ******************************************************************
ok: [web1] => { "msg": "C on web1" }web2 and web3 completed the entire play — A, B and C — before web1
finished task A. The task banner is reprinted when a host reaches a task
the output has moved past.
The upstream description matches what was observed: “Ansible will not wait for other hosts to finish the current task before queuing more tasks for other hosts… a host that is slow or stuck on a specific task will not hold up the rest of the hosts and tasks.”
That output block is also the honest picture of what a free run looks
like to read. Task banners repeat, hosts appear out of order, and there is
no moment at which the transcript tells you the fleet is in a consistent
state.
host_pinned
The description is precise about what it does and it is worth reading carefully, because the name suggests something narrower:
“Task execution is as fast as possible per host in batch as defined by
serial(default all). Ansible will not start a play for a host unless the play can be finished without interruption by tasks for another host, i.e. the number of hosts with an active play does not exceed the number of forks. Ansible will not wait for other hosts to finish the current task before queuing the next task for a host that has finished. Once a host is done with the play, it opens its slot to a new host that was waiting to start. Other than that, it behaves just like thefreestrategy.”
So: free, with an additional rule that a host is only started when a
worker slot is available for it to run to completion, and slots are
recycled as hosts finish.
Where that earns its place is a play that holds a scarce resource for its
whole duration — a licence checkout, a maintenance-window slot, a database
connection from a small pool. Under free, forks hosts start and all of
them hold the resource concurrently for as long as any of them is slow.
Under host_pinned, the number of hosts with a play in progress stays
bounded and each one finishes before another begins.
It is still barrier-free. Everything the callout above says about staged
changes applies to host_pinned unchanged.
debug
Not a performance strategy. It drops into an interactive debugger when a task fails, letting you inspect and re-run it. It forces serialised execution as a side effect of being interactive, which makes it useless for anything except debugging — and the debugging technique itself belongs to this course’s troubleshooting part.
Do not leave strategy: debug in a committed playbook. A run that stops
for an interactive prompt in an unattended context is a run that hangs
until someone notices.
Knowledge check
Knowledge check · 4 questions
Q1. Three hosts run a play of tasks A, B and C. web1 takes six seconds on A; web2 and web3 take none. Under strategy: free, what does the run look like?
Q2. Why is strategy: free inappropriate for a play that drains a host, deploys, health-checks and returns it to the pool?
Q3. Which statements about the strategy plugins in ansible.builtin are correct? Select all that apply.
Q4. Under linear, the completion of a task means every host in the current batch has finished that task or has dropped out of the play.
Passing score: 75%. Answers are checked in this browser.