unit 5 · Logic

Variables & the precedence that matters

Five levels run your life; the other seventeen are trivia

essential · 8 min · linux + windows

you may want to read 9 · group_vars & host_vars , 15 · Registering output & debug first — but nothing is locked

tl;dr

  • The daily ladder, low to high: role defaults → inventory vars → role vars → set_fact → extra vars (-e).
  • Tunables in defaults/, environment config in group_vars/, internal constants in vars/. -e only on purpose.
  • Never pass secrets with -e — they leak into logs. When the wrong value wins, -vvv shows what each task used.

why it matters The classic "why isn’t my group_vars value winning?" is a precedence bug — until you know the ladder, the wrong value silently configures real hosts and you debug everything except the cause.

You can already set the same variable in several places: group_vars/ and host_vars/ from lesson 9, a registered result from lesson 15. Ansible accepts variable definitions in about two dozen places. When the same name is defined in more than one, exactly one value wins, decided by a fixed order called precedence .

The official docs list all 22 levels. Memorizing them is an interview party trick, not a skill. Five levels decide almost every real conflict you’ll hit. Lowest to highest:

  1. Role defaults (defaults/main.yml) — the floor of the ladder. Overriding them is their entire job.
  2. Inventory group_vars / host_vars — lesson 9’s files, where environment configuration lives. It beats defaults.
  3. Role vars (vars/main.yml) — high enough that your inventory cannot override them. That’s a feature: internal constants.
  4. set_fact and registered variables — runtime values created mid-play. They beat everything file-based above.
  5. Extra vars (-e) — level 22 of 22. Overrides everything.

Two of those five come from roles, which you won’t meet properly until lesson 24. For now: a role is a reusable, packaged unit of tasks that brings two variable slots — one at the very bottom of the ladder, one above your inventory. That placement is the whole story.

The rule of thumb

Each level exists for a different kind of value, and the ladder only hurts when values are filed in the wrong slot:

  • User-tunable knobs → defaults/ — low on purpose, so anyone consuming the role can override them.
  • Environment config (per-datacenter, per-tier, per-host) → inventory group_vars/ and host_vars/.
  • Internal constants the role needs but nobody should touch → vars/ — high on purpose, so nobody accidentally does.
  • Deliberate one-off overrides → -e, and nothing else.

Extra vars: the override of last resort

Level 22 has a name: extra vars . They ride the command line and outrank every other definition — inventory, role vars, even set_fact:

# group_vars/web.yml sets max_clients: 200 — -e wins anyway
ansible-playbook site.yml -e "max_clients=512"
group_vars said 200; level 22 doesn't care

That power makes -e the right tool for deliberate, visible overrides — “deploy this one build number”, “run against the standby tier this once”. It is also the classic way to cause the wrong-value mystery. Someone’s wrapper script quietly passes -e, and no edit to any file can ever win against it.

Variables you never set

Ansible also injects magic variable s describing the run itself. Five earn their keep immediately:

  • inventory_hostname — the host this task runs for, exactly as the inventory spells it.
  • hostvars — every host’s variables, reachable from any host: hostvars['db1'].some_var.
  • groups — all inventory groups and their member hosts.
  • group_names — the groups this host belongs to.
  • ansible_play_hosts — the hosts still active in the current play.
- name: Where am I in this run?
  ansible.builtin.debug:
    msg: "{{ inventory_hostname }} is in {{ group_names }}"
magic variables need no setup — just print them

You’ll meet them constantly — templating a config that lists every web server, or branching on group membership. Recognize the names; depth can wait.

When the wrong value wins

The day a variable comes out wrong, don’t re-read files in random order — make Ansible tell you. Run with -vvv: the output shows the exact values each module was called with, so you can see which definition won. With lesson 15’s register-and-debug reflex, that answers the classic mystery in minutes. The culprit is usually a role var or a forgotten -e sitting above the group_vars file you keep editing. Next lesson adds the biggest variable source you haven’t met yet — the facts Ansible gathers from every host before the first task runs.

practice — which value wins? (1/5)

The classic: defaults vs environment

practice, not progress — authored from the ladder above, nothing simulated

at work, this sounds like

“That’s in the role’s defaults, just override it.”
The value lives in defaults/main.yml — the lowest precedence level, designed for your inventory to beat.
“Why isn’t my group_vars value winning?”
Something higher on the ladder — role vars, set_fact, or -e — defines the same name. Trace it with -vvv.

self-check — recall, not a test

  1. 1 Order the five daily precedence levels, lowest to highest.

  2. 2 A value differs per environment; another is a user-tunable knob; a third is an internal constant no one should touch. Where does each live?

  3. 3 Why is -e the wrong way to pass a database password, even though it works?

lab (optional — never required)

Set a var in group_vars, print it with debug, then re-run with -e overriding it — watch level 22 win. Then debug group_names and inventory_hostname.

needs: The control node from lesson 5 with the lesson 9 inventory.

next up 17 · Facts, set_fact & caching 7 min · unit 5