Ansible Execution Strategies: Linear, Free, and Serial
Choose between Ansible linear, free, and serial execution strategies based on synchronization, runtime, and blast radius. Includes validation steps.
15 Apr 2026, 16:27 UTC

The decision and your constraints
You need to run an Ansible playbook across more hosts than a single task can handle efficiently. The default behavior waits for every host to finish one task before starting the next, which is safe but slow. Alternatively, you may need to update a service fleet without taking every node offline at once. The choice is between Ansible's execution strategies—linear, free, and serial—and the forks setting that controls how many hosts Ansible contacts at the same time.
Use this guide when you are deciding how a playbook should move through a host inventory. The constraints are usually synchronization, total runtime, and blast radius. Synchronization means whether Host B must wait for Host A to finish a task. Runtime is the total playbook duration. Blast radius is how many hosts are affected if a task fails or consumes shared resources.
Compare the supported execution strategies
| Strategy | How it runs | Best for | Main risk |
|---|---|---|---|
| linear | Runs one task across all hosts before starting the next task. Hosts stay in step. | Tasks where one host depends on another, such as promoting a database primary before reconfiguring replicas. | Head-of-line blocking: one slow or unreachable host delays every other host for that task. |
| free | Each host runs tasks independently and as fast as possible. No cross-host ordering. | Independent configuration work like package installation or file templating where host order does not matter. | Thundering herd on shared resources such as package mirrors or network bandwidth. |
| serial | Processes hosts in batches, for example two at a time. Can wrap either linear or free. | Rolling updates behind a load balancer where you want to drain and update a small batch before moving to the next. | Total runtime grows with the number of batches; plan maintenance windows accordingly. |
The forks setting controls the number of parallel Ansible workers. The default is five. You can raise it to speed up free strategy or lower it to protect fragile targets. serial and forks interact: serial limits the batch size, while forks limits how many hosts inside that batch run at once.
Trade-offs by scenario
- Database failover: Use linear. A replica must not be reconfigured until the new primary is reachable, so cross-host ordering matters more than raw speed.
- Security patch installation: Use free with a conservative forks value. Each host is independent, so waiting for the slowest host wastes time.
- Rolling web tier update: Use serial with linear. Update one load-balancer batch at a time, verify health, then proceed. This keeps capacity available while limiting failure scope.
Concrete implementation: rolling update with serial
This example updates a web tier in batches of two. The playbook assumes an inventory group named webservers and a load balancer that can drain nodes. Placeholders in angle brackets must be replaced with your own values.
- name: Rolling web tier update
hosts: webservers
serial: 2
strategy: linear
forks: 2
tasks:
- name: Drain host from load balancer
ansible.builtin.command: /usr/local/bin/drain-lb.sh <load-balancer-name> {{ inventory_hostname }}
delegate_to: localhost
run_once: true
- name: Stop application service
ansible.builtin.service:
name: webapp
state: stopped
- name: Apply package update
ansible.builtin.apt:
name: webapp
state: latest
become: true
- name: Start application service
ansible.builtin.service:
name: webapp
state: started
become: true
- name: Return host to load balancer
ansible.builtin.command: /usr/local/bin/drain-lb.sh <load-balancer-name> {{ inventory_hostname }} --enable
delegate_to: localhost
run_once: true
Run this from the Ansible control node with read access to the inventory and SSH or WinRM access to the targets:
ansible-playbook -i <inventory-file> rolling-update.yml --forks 2 -v
Required permissions: the remote user needs sudo or equivalent for the service and package tasks. The control node user needs permission to read the inventory and execute the playbook. Risk: if the drain script fails, the host may be removed from the load balancer but never returned, so verify the script is idempotent or add a rescue block.
Validate what actually happened
- Run the playbook with
-vand watch the task output. In linear mode, you should see the same task complete on all hosts in a batch before the next task begins anywhere. - Check the configured fork value on the control node:
ansible-config dump | grep DEFAULT_FORKS
Override at runtime with --forks if the default does not match your concurrency target. To confirm serial batching, look for host groups in the verbose output: hosts 1 and 2 should start and finish the play before hosts 3 and 4 begin.
To test free strategy independently, copy the playbook, set strategy: free, remove serial, and compare per-host completion timestamps in the verbose log. Hosts should finish at different times rather than in lockstep.
Limitations and pre-flight checks
- Linear strategy can make large fleets impractically slow because one unreachable host stalls the entire task.
- Free strategy can saturate package mirrors or network links. Lower forks or use a local mirror if you see transfer errors.
- Serial increases total runtime in proportion to batch count. A 100-host fleet with serial: 2 runs at least 50 batches for each serial task.
- Execution environment images may change default Python dependencies or forks behavior. Run
ansible-config dumpin the target environment before rollout.
There is no state-changing rollback procedure for choosing a strategy itself. If a playbook run fails partway through, re-run it after fixing the failed task; Ansible tasks are generally idempotent when written with built-in modules.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.