100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
DevOps

Variables and Precedence

How Ansible collects variable values from more than twenty possible sources and resolves conflicts using a strict, well-defined precedence order.

Variables & TemplatesIntermediate10 min readJul 10, 2026
Analogies

Why Ansible Variables Matter

Ansible variables let you write a single playbook that behaves differently for different hosts, environments, or runs, instead of hardcoding values like package versions, IP addresses, or feature flags directly into tasks. A variable can come from inventory files, role defaults, command-line flags, discovered facts, or registered task output, and Ansible merges all of these into one namespace for each host before executing a task.

🏏

Cricket analogy: Just as a franchise fields a different XI for a day game at the Wankhede versus a day-night game at Eden Gardens without rewriting the team's playbook, Ansible variables let one playbook adapt its behavior per host without duplicating tasks.

Where Variables Can Live

Variables can be declared in inventory files (as host or group vars), in group_vars/ and host_vars/ directories, inside a play with the vars: keyword, in role defaults/main.yml and vars/main.yml, loaded dynamically with vars_files or include_vars, captured at runtime with set_fact or register, or passed on the command line with -e. Each location has a different intended purpose: role defaults are meant to be overridden, while extra vars are meant to override everything else.

🏏

Cricket analogy: A team's base strategy sits in the coaching manual (role defaults), but the captain can override field placements mid-over from the crease (-e extra vars), showing that some sources are meant to be easily overruled.

The Precedence Order in Practice

Ansible resolves conflicting values using roughly twenty-two precedence levels, documented in order from lowest to highest. Role defaults sit at the bottom, followed by inventory vars, inventory group_vars and host_vars files, play vars, block vars, task vars, and role vars/main.yml, with facts and registered variables sitting in the middle, and command-line extra vars (-e) always winning regardless of where else the variable is defined. Understanding this order is essential when a value 'doesn't seem to apply' during a run.

🏏

Cricket analogy: Match regulations rank the ICC playing conditions above a home ground's local rule, so if Eden Gardens has a quirky boundary rule that conflicts with ICC law, ICC law wins, just as extra vars always outrank every other Ansible source.

Common Precedence Pitfalls

The most frequent surprise is that role vars/main.yml has higher precedence than role defaults/main.yml, so a value placed in vars/main.yml cannot be overridden by group_vars the way a defaults/main.yml value can — this is why role authors are told to put tunable knobs in defaults and internal constants in vars. Another common trap is assuming host_vars always beats group_vars: it does, but only when both target the exact same host, and a more specific child group does not automatically beat a less specific group unless inventory group nesting is set up correctly.

🏏

Cricket analogy: A bowler assumes a yorker will always beat a batter, but against Jos Buttler's reach and reflexes it doesn't automatically work, just as assuming host_vars always 'wins' without checking group nesting can surprise you.

yaml
# roles/webserver/defaults/main.yml  (lowest precedence, meant to be overridden)
http_port: 8080
max_connections: 100

# roles/webserver/vars.yml  (higher precedence, treat as role internals)
app_install_dir: /opt/webserver

# inventory/group_vars/production.yml
http_port: 443
max_connections: 500

# inventory/host_vars/web01.yml
http_port: 8443

# Run with an extra var, which always wins over every file above:
# ansible-playbook site.yml -e "http_port=9090"

- name: Show effective value
  hosts: web01
  tasks:
    - name: Print resolved http_port
      ansible.builtin.debug:
        var: http_port

Use ansible-inventory --host <hostname> --yaml or ansible <hostname> -m ansible.builtin.debug -a "var=hostvars[inventory_hostname]" to inspect exactly which variables Ansible resolved for a host before you run a full playbook.

Command-line extra vars (-e) silently override everything, including values you carefully set in host_vars for a specific machine. If a variable 'won't change' no matter what you edit in inventory files, check whether it is being passed with -e in your CI pipeline or wrapper script first.

  • Ansible merges variables from over twenty possible sources into one namespace per host before running tasks.
  • Role defaults/main.yml is the lowest-precedence source and is meant to be freely overridden by inventory variables.
  • Role vars/main.yml has much higher precedence than defaults and is not meant to be casually overridden.
  • host_vars beats group_vars only when both target the same host; group nesting affects which group_vars file wins.
  • Command-line extra vars (-e) always win over every other source, regardless of specificity.
  • Use ansible-inventory --host or the debug module with hostvars to inspect resolved values before assuming precedence.
  • Facts and registered variables sit in the middle of the precedence chain, above most static inventory sources.

Practice what you learned

Was this page helpful?

Topics covered

#DevOps#AnsibleStudyNotes#VariablesAndPrecedence#Variables#Precedence#Ansible#Matter#StudyNotes#SkillVeris#ExamPrep