Chapter 4: Playbooks — The Core
Topic 21

Running Playbooks

Workflow

You run a playbook with ansible-playbook site.yml, and the flags you reach for daily are the ones that narrow what runs and show you what it did. --limit scopes hosts, --tags scopes tasks, -v raises verbosity, and --check previews without changing anything. The exit code is the machine-readable verdict your CI watches, separate from the human-readable recap on screen.

These five controls cover almost every real run. The base command runs everything; the flags let you canary one host, reconfigure one subsystem, or look closer when something is wrong. This topic walks each in turn, against the Larkspur site.yml you have been building.

The Base Command

ansible-playbook -i inventory site.yml reads the inventory, runs every play in order, and ends with a per-host recap of ok, changed, unreachable, failed, and skipped counts. The recap is the first thing to read, because a nonzero failed or unreachable is the headline of the run — everything scrolling above it is detail.

A clean run of the Larkspur playbook and its per-host recap
$ ansible-playbook -i inventory site.yml

PLAY RECAP ******************************************************
db1.larkspur.io  : ok=6  changed=2  unreachable=0  failed=0
web1.larkspur.io : ok=9  changed=3  unreachable=0  failed=0
web2.larkspur.io : ok=9  changed=0  unreachable=0  failed=0

Read that recap bottom-up by column: zero failed and zero unreachable across all three hosts means the run succeeded. web2 shows changed=0 because it was already in the desired state, while web1 changed three things — exactly the idempotent behavior you want to see settle toward zero on a re-run.

--limit — Scoping Hosts at Run Time

--limit web1.larkspur.io restricts the run to one host regardless of what each play's hosts: line says. This is the standard way to canary a change: apply it to one web host, confirm it green, then rerun without the limit to let it reach the whole web group. The flag narrows the host set; it never widens it.

That direction matters. --limit can only filter the play's existing hosts down to a subset — it cannot add a host that is not already in the matched inventory. Reaching for it to include an extra machine is a misread of what it does; it is a safety valve for scoping down, not a way to expand a run.

--tags and --skip-tags — Scoping Tasks

Tasks carry tags, and --tags nginx runs only the tasks tagged nginx, leaving everything else untouched. So you can reconfigure just the proxy without going near the database, by tagging Larkspur's tasks nginx, gunicorn, and postgres and selecting one. --skip-tags is the inverse — run everything except the named tags — and --list-tags prints what is available before you commit to a run.

Tagging has one sharp edge worth naming now. If the task that would notify a handler is itself skipped — because its tag was not among those selected — the handler is never notified, and the reload you expected silently does not happen. Tag related tasks together so a partial run stays coherent.

Verbosity -v / -vvv

Verbosity is a dial. -v adds each module's return data, -vv adds the task input arguments, and -vvv adds the full SSH connection detail down to the negotiated commands. Reach for -vvv only when you are debugging a connection problem or chasing why a task reported changed, because at that level the output is a wall of text that buries the signal.

For daily runs, no verbosity flag at all is right — the recap tells you what happened. Raise the dial for the one run you are actively debugging, then drop it again. Leaving -vvv on in a pipeline is how a real error ends up hidden under thousands of lines of handshake.

--check and Exit Codes

--check runs the playbook in dry-run mode: each module reports what it would change without changing it, so you can preview a risky apply against production safely. It is the forward reference to Chapter 7's check mode and --diff, and the reason it is safe is that no module is allowed to mutate the host when check mode is on.

The exit code is what CI keys on, not the recap text. A run returns 0 for success, 2 when a task failed, and 4 when one or more hosts were unreachable. Gate the pipeline on that number — 0 proceeds, 2 and 4 fail — because the on-screen recap is for humans and the exit code is the contract for machines.

Common Mistakes
  • Running ansible-playbook site.yml against a prod inventory without --limit to "just test," and reconfiguring all six prod web hosts in one shot instead of one.
  • Reading --limit as a way to add hosts to a run — it only filters the play's existing host set down, and never expands it beyond what the inventory already matched.
  • Trusting a green-looking terminal while a host actually came back unreachable with exit code 4 and was silently skipped — read the recap counts, not the scrollback.
  • Running tagged tasks with --tags nginx and being surprised a handler did not fire, because the notifying task was under a different tag that got skipped.
  • Leaving -vvv on in CI logs, burying the one real error under thousands of lines of SSH negotiation nobody scrolls through.
Best Practices
  • Canary every production change with --limit <one host> first, confirm it green, then rerun without the limit to roll out to the whole group.
  • Tag tasks by component — nginx, gunicorn, postgres — so --tags can reconfigure one subsystem without a full run, and check what exists with --list-tags.
  • Gate CI on the exit code, not log parsing — 0 proceeds, 2 and 4 fail the pipeline — since the recap text is for humans and the code is for machines.
  • Run with --check before a real apply on anything risky, and raise verbosity to -vvv only for the single failing run you are debugging.
  • Read the recap as the verdict every time: a nonzero failed or unreachable is the headline, no matter how clean the scrollback above it looked.
Comparable tools terraform plan the dry-run analog to --check puppet agent --noop Puppet's preview mode salt-call · salt --state-verbose Salt's run controls Shell script offers none of this without hand-rolling it

Knowledge Check

What does --limit web1.larkspur.io do, and what can it not do?

  • It filters each play's host set down to that one host; it cannot add a host that the inventory did not already match
  • It adds web1.larkspur.io into the run even when no play in the playbook targets it, expanding the active host set beyond what the inventory match produced
  • It limits how many of the tasks get to run, rather than narrowing which hosts they run against
  • It caps the run to whichever host sorts first alphabetically, regardless of the value you passed in

What is the difference between scoping a run with --limit and scoping it with --tags?

  • --limit narrows which hosts the run touches; --tags narrows which tasks run on those hosts
  • They are just aliases — both of them restrict which hosts the run touches, only with different syntax
  • --limit selects which tasks run and --tags selects which hosts; the two names are reversed from what they suggest
  • --tags only takes effect in check mode, whereas --limit is the one that works in a real run

Your CI runs a playbook and gets exit code 4. What does that tell the pipeline?

  • One or more hosts were unreachable, so the pipeline should fail rather than treat the run as a success
  • Four tasks changed something on the hosts during the run, which is purely informational and entirely safe for the pipeline to proceed on
  • The run succeeded in the end, but it took four retries before the hosts would connect
  • Four hosts were skipped by a when: condition, which is a normal and expected outcome

Why is --check safe to run against a production inventory?

  • In check mode no module is allowed to mutate the host — each only reports what it would change
  • It runs only against a throwaway copy of production that Ansible clones first before touching anything
  • It automatically restricts the whole run down to a single canary host chosen from the inventory
  • It disables the SSH connection entirely and reads from a cached state file instead of the live hosts

You got correct