unit 7 · Secrets & scale

Strategies, serial & delegation

Who runs what, when — and where

supplementary · 7 min · linux + windows

you may want to read 12 · Playbook anatomy first — but nothing is locked

tl;dr

  • linear (default) marches every host through a task before the next starts; free lets each host race ahead.
  • serial: 1 turns a play into a rolling update — the whole play finishes on one batch before the next begins.
  • delegate_to runs a task on a different host; run_once runs it a single time for the whole play.

why it matters Rolling updates are how teams ship without downtime. When a colleague says "we use serial 1" or "drain it from the LB first", you need to picture what runs where, and in what order.

Lesson 12 gave you the top-down rule: tasks run in order. How that order plays out across hosts is the strategy’s job. The default, linear, makes every run a marching band: all hosts finish task 1 before any host starts task 2. You’ll meet linear, free, and host_pinned; only the first two matter in practice.

Within a task, forks — the ansible.cfg setting from lesson 6 — caps how many hosts are worked on at once. Twenty forks against a hundred hosts means waves of twenty. linear still holds the line until the last host finishes. Forks is a throughput ceiling; it never changes ordering.

strategy: free drops the lockstep. Each host runs its own task list as fast as it can, never waiting for the others — fine for patching fleets where hosts don’t care about each other:

- name: Patch the estate
  hosts: all
  strategy: free # each host races ahead independently
free: no lockstep — for fleets with no cross-host choreography

host_pinned is a stricter cousin of free — a host that starts the play keeps its worker until it finishes. File the name; you’ll rarely see it.

serial: the rolling update

Linear has a gap. A play that restarts a service restarts it on every host in the same task: your whole web tier goes down together, briefly but completely. The fix is serial , a play-header keyword that batches hosts: the whole play finishes on one batch before the next begins:

- name: Deploy the new build
  hosts: web
  serial: 1 # also takes "25%", or a list like [1, 5, "100%"]
  tasks:
    - name: Update the app
      ansible.builtin.package:
        name: myapp
        state: latest
serial in the play header — this is what 'rolling update' means

serial: 1 is one host at a time — safest, slowest, and the one you’ll hear in standups. A percentage (serial: "25%") trades safety for speed. The list form is a canary pattern: one host, then five, then everyone. Each wave starts only because the last survived. If a whole batch fails, the play aborts and the remaining batches never run.

Delegation: run it over there

Before touching a web host, the load balancer should stop sending it traffic. But the play targets web hosts, not the LB. That’s delegation : delegate_to executes one task on a different machine while the play keeps “thinking about” the original host. Its sibling run_once runs a task once for the whole play. Caveat: under serial, it fires once per batch, not once overall:

- name: Pull this host out of the load balancer
  ansible.builtin.command: lb-cli drain {{ inventory_hostname }}
  delegate_to: lb01 # runs ON lb01, once per web host

- name: Tell the channel the deploy started
  ansible.builtin.command: notify-chat "deploying web tier"
  run_once: true # one execution for the whole play
the two delegation moves you'll actually see

Three neighbours to recognise but not stop for. throttle caps how many hosts run one specific task at once — a per-task forks. local_action is an older shorthand for delegate_to: localhost. ansible.builtin.wait_for pauses until a port or file is ready, classically used between “restart the service” and “put the host back in the LB”.

Nobody expects you to design a rolling update this month — recognition is the goal. When you open a deploy playbook, read the play header first: hosts, serial, strategy tell you the blast radius before you read a single task. And when someone says “we use serial 1”, you hear the cost: twenty hosts, twenty full passes. That slowness is the feature.

at work, this sounds like

“We use serial 1.”
The deploy play is a rolling update — it runs to completion on one host at a time, so the rest of the fleet stays up.

self-check — recall, not a test

  1. 1 forks and serial both limit "how many hosts at once" — what is the actual difference?

  2. 2 A task has delegate_to: lb01. Where does it execute, and what does inventory_hostname hold there?

  3. 3 Your colleague says the deploy play "uses serial 1" and there are 20 web hosts. Describe the run.

next up 29 · Run control: check, diff, tags, limit 7 min · unit 7