Ansible: variable precedence¶
Mental model¶
Ansible has ~22 precedence levels but you only need the bookends: role defaults lose, extra vars (-e) win. Everything else falls in between in a predictable gradient from general to specific.
What it looks like¶
Variables appear to come from nowhere or change unexpectedly. People chase values through five files before finding the source. The official precedence list is intimidating.
What it really is¶
Key levels from lowest to highest priority:
- Role defaults (
roles/x/defaults/main.yml) -- lowest - Inventory
group_vars/ - Inventory
host_vars/ - Play
vars: - Role
vars/(roles/x/vars/main.yml) - Task
vars: set_fact/register- Extra vars (
-e) -- highest, always wins
The full list has ~22 entries but most are edge cases (e.g.,
include_vars, vars_prompt, vars_files each have their
own slot). In practice the above 8 cover almost every
situation.
Why it seems confusing¶
- Too many levels with subtle ordering
role defaults/vsrole vars/sounds like the same thing but they sit at opposite ends of the precedence spectrum- Variables can be set in inventory, playbooks, roles, tasks, facts, and CLI -- all at once
What actually matters¶
The simplified rule: defaults < inventory vars < play/role vars < extra vars
Design intent:
- Role defaults/ = safe fallbacks anyone can override
- Role vars/ = internal constants the role needs (hard to
override on purpose)
- Extra vars (-e) = CLI emergency override, always wins
Put tunable knobs in defaults/. Put internal constants in
vars/. Let users override via group_vars/ or -e.
Common mistakes¶
- Putting user-tunable values in
roles/x/vars/instead ofroles/x/defaults/-- makes them nearly impossible to override from inventory - Setting the same variable in multiple places without knowing which one wins
- Forgetting that
set_factoverrides almost everything except extra vars - Assuming inventory group_vars beat play vars (they do not)
Small examples¶
# roles/nginx/defaults/main.yml -- lowest priority
nginx_port: 80
# group_vars/production.yml -- overrides role default
nginx_port: 8080
# play vars -- overrides group_vars
- hosts: webservers
vars:
nginx_port: 9090
# CLI extra vars -- overrides everything
# ansible-playbook site.yml -e nginx_port=443
Result with all four set: nginx_port = 443 (extra vars win).
One-line summary¶
Role defaults are the bottom, extra vars (-e) are the top; put tunables in defaults, use -e for emergency overrides.