forks, serial, and Rolling Updates
Two numbers govern how a play touches a fleet, and they are constantly mixed up. forks is control-node parallelism — how many hosts Ansible talks to simultaneously, default 5. serial is batch size — how many hosts a play runs against per wave before moving to the next batch, the mechanism a rolling deploy is built on. Raising forks makes the run faster; setting serial makes the run safer.
The two answer different questions, and the rest of this topic keeps them apart deliberately before assembling them into the rolling deploy this chapter is heading toward. Get the distinction wrong and you either run a slow deploy that you think is safe, or a fast one that quietly lands everywhere at once.
forks — Control-Node Concurrency
forks caps how many parallel connections the control node opens at once. The default is 5, set in ansible.cfg or overridden with --forks. With Larkspur's six prod web hosts and forks: 5, the control node works five hosts in parallel and the sixth waits for a slot to free up — but every host still runs the same play to completion. forks changes nothing about which hosts run or in what grouping; it only changes how many connections are live at any instant.
This is purely a speed knob. Raise it and a wide play finishes sooner because more hosts are worked in parallel; leave it too low on a large fleet and the run is connection-starved, crawling through hosts a handful at a time while you wrongly conclude Ansible is slow.
serial — Batch Size Per Wave
serial is a different idea entirely. serial: 2 runs the entire play against two hosts, finishes them completely, then runs the entire play against the next two, and so on. This is what turns "deploy to all six at once" into "deploy two at a time," so a bad release fails on a small wave instead of landing everywhere simultaneously. Where forks is about connections, serial is about waves — it slices the host list into batches and runs the whole play per batch.
That batching is the foundation of every rolling deploy. The fleet is never touched all at once; it is rolled through in controlled chunks, and the size of each chunk is the blast radius you have chosen to accept.
serial as a Ramping List
serial also accepts a list, which is how you build a canary. serial: [1, 2, 3] makes the first batch a single host, the next batch two hosts, the next three. You prove the change on one host, widen to two once it holds, then accelerate. If the canary breaks, the play has touched exactly one host, not the fleet. The list shape lets a rollout start cautious and speed up only after the risk has been retired.
- hosts: web serial: - 1 # wave 1: the canary — one host - 2 # wave 2: two more, once the canary holds - 3 # wave 3: the remaining three become: true tasks: - name: Deploy the new larkspur release ansible.builtin.git: repo: https://github.com/larkspur-io/app.git dest: /opt/larkspur version: "{{ release_tag }}"
serial with Percentages
serial also takes a percentage. serial: "25%" sizes each batch as a fraction of the targeted hosts, so the same play scales its batch size whether the prod web tier is six hosts or sixty without anyone editing a number. The percentage is computed against the targeted host count and rounds up, which matters on odd-sized tiers — "50%" of six is three, but on a tier of seven it is four, not three-and-a-half. When the exact wave size matters, confirm it with a dry run rather than mental math.
Putting Them Together for a Rolling Deploy
The two knobs combine cleanly once you see what each owns. serial defines the rollout waves — how many hosts are in play at a time. forks defines how fast each wave executes internally — how many of that wave's connections run in parallel. A rolling web deploy is serial: 1 over the web group, with forks set high enough that the rest of the play — fact gathering, package steps on whatever hosts are in the wave — is not artificially throttled.
forks — speedserial — safetyserial: 1 deploys one host at a time and bounds the blast radius.At serial: 1 there is only ever one host in a wave, so forks barely matters for that play; its value shows when waves contain many hosts. Keep the two settings conceptually separate: forks is the speed setting, serial is the safety setting, and the next topic adds the delegation that lets each one-host wave drain itself from the load balancer first.
forks — control-node parallelism: how many hosts Ansible connects to at the same time, default 5. It is a speed knob and does not change which hosts run or in what grouping. Raising forks from 5 to 20 makes a six-host play finish sooner; it does not batch anything.
serial — batch size: the play runs on N hosts as a complete wave, then the next N, which is how you bound blast radius and build a rolling deploy. Setting serial: 1 makes it deploy one host at a time. The two solve different problems and are set independently — one is speed, the other is safety.
- Setting
forks: 1thinking it produces a one-at-a-time rolling deploy — it serializes connections, not the play, so all hosts still get the change; onlyserial: 1gives you batched waves. - Leaving
forksat the default 5 on a sixty-host play and concluding Ansible is slow — the run is connection-starved, not actually slow; raiseforksto match the fleet. - Using
serial: "50%"on a six-host prod tier and assuming exactly three — percentages round up, and on odd counts the batch sizes are not what quick mental math suggests; verify with a dry run. - Cranking
forksvery high to speed a rolling deploy while keepingserial: 1— the deploy is still one host per wave by design; highforksonly helps when batches contain many hosts. - Treating
serialas a performance setting and tuning it for speed — it is a safety setting, and widening it to go faster directly widens the blast radius of a bad release.
- Set
forksto roughly match the size of your batches — or the fleet, for non-serialized plays — so the run isn't connection-starved, and tune it inansible.cfgrather than per invocation. - Use
serial: 1for the canary wave of any production rollout, widening with a list like[1, 2, 3]once the first host proves the change holds. - Express
serialas a percentage on tiers that grow, so the same play batches correctly at six hosts and at sixty without edits. - Keep the two knobs conceptually separate in code review —
forksis a speed setting,serialis a safety setting — so nobody "speeds up" a deploy by breaking its batching. - Confirm the actual wave sizes a percentage
serialproduces with a dry run before trusting it on prod, since rounding-up changes the math on odd-sized tiers.
maxSurge / maxUnavailable, the closest analog to serial batching
Terraform no per-resource batch-rollout concept
Puppet · Chef pull models converge nodes on their own timers, with no orchestrated wave
Knowledge Check
What is the difference between forks and serial?
forksis control-node parallelism — how many hosts run at once;serialis batch size — how many hosts a wave runs before the next- They are really the same underlying setting, with
serialbeing just the newer, renamed form offorksintroduced in current Ansible releases forksis what batches the play into rolling waves, andserialis what caps how many parallel connections runforksapplies only to full plays whileserialapplies only to one-off ad-hoc commands on the CLI
Why is forks: 1 not a rolling deploy?
- It serializes connections, not the play, so every host still gets the change — only
serial: 1produces batched waves - It deploys the change to just the first host and then stops the play entirely before the rest
- It only produces a rolling deploy when it is explicitly combined with
strategy: freeon the play - It runs the whole play twice on each host, one full pass after another, instead of running it through just a single time
What does serial: [1, 2, 3] produce, wave by wave?
- A first wave of one host (the canary), then a wave of two hosts, then a wave of three
- Three hosts in the first wave, then a wave of two, then one — the batches winding back down
- Six separate waves of one host each, with the actual numbers in the list ignored entirely
- One single wave covering all six hosts at once, since the numbers in the list add up to six
How does a percentage serial like "25%" scale with the tier?
- It sizes each batch as a fraction of the targeted hosts and rounds up, so the same play batches correctly at six hosts or sixty without edits
- It always rounds the fraction down to the nearest whole host, so on a small tier of three or four a wave's batch can come out as zero hosts and quietly touch nothing
- It is computed as a fraction of the
forksvalue, not of the targeted host count at all - It fixes every batch at a literal 25 hosts regardless of how large or small the tier is
You got correct