unit 5 · Logic
Variables & the precedence that matters
Five levels run your life; the other seventeen are trivia
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:
- Role defaults (
defaults/main.yml) — the floor of the ladder. Overriding them is their entire job. - Inventory
group_vars/host_vars— lesson 9’s files, where environment configuration lives. It beats defaults. - Role vars (
vars/main.yml) — high enough that your inventory cannot override them. That’s a feature: internal constants. set_factand registered variables — runtime values created mid-play. They beat everything file-based above.- 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/andhost_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"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 }}"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 Order the five daily precedence levels, lowest to highest.
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 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.