CoursesAnsibleIntermediate

Variables, facts & precedence

Where values come from and who wins.

Intermediate14 min · lesson 5 of 12

Variables let one playbook adapt to many hosts and environments. They come from many places — inventory (group_vars, host_vars), the playbook, roles, extra-vars on the command line, and facts gathered from the hosts. The power and the pitfall are the same: with so many sources, you must know the precedence order, because when two define the same variable, the higher-precedence one silently wins.

VARIABLE PRECEDENCE: LOWEST TO HIGHEST
1Role defaults
defaults/ — easiest to override
2Inventory vars
group_vars, host_vars
3Play & role vars
defined in the play or role
4Extra-vars (-e)
highest — unbeatable at run time
When two sources set the same variable, the higher-precedence one silently wins.
group_vars/web.yml
# variables for every host in the web group
nginx_worker_processes: 4
app_port: 8080
---
# host_vars/web1.acme.internal.yml — overrides for one host
app_port: 8081

Facts

Facts are variables Ansible discovers about each host at run time — OS family, IP addresses, memory, mounted disks — gathered automatically at the start of a play (or on demand with the setup module). You use them to write portable automation: install apt packages on Debian and dnf on RHEL by branching on ansible_facts[‘os_family’], size a config from the host’s real memory, and so on.

terminal
$ ansible web1 -i inventory.ini -m setup -a "filter=ansible_os_family"
web1 | SUCCESS => { "ansible_facts": { "ansible_os_family": "Debian" } }
# use it in a task: when: ansible_facts['os_family'] == "Debian"
Learn the precedence order — or debug it forever
Ansible has a long, fixed variable-precedence hierarchy (roughly: role defaults lowest, then inventory, then play/role vars, with extra-vars -e highest and unbeatable). The classic time-sink is a variable that “won’t change” because something higher wins. Keep overridable defaults in role defaults/, environment values in group_vars, and reserve -e for genuine run-time overrides — and when confused, use --extra-vars sparingly and check the source.