Variable precedence and scoping

Ansible · Advanced · 10 min

"Why is this variable not the value I set?" is the most common Ansible debugging question.

Ansible merges variables from over twenty sources. You don't need the whole list, just the landmarks, from lowest to highest:

1. role defaults/ 2. inventory variables: group vars (all, then parent groups, then child groups), then host vars; files in group_vars//host_vars/ beat variables written inside the inventory file 3. facts and cached facts 4. play vars, vars_prompt, vars_files 5. role vars/ and include_vars 6. block and task vars 7. set_fact and register 8. role parameters and include_role params 9. extra vars (-e), which always win.

Group order: when a host is in two sibling groups, the group whose name sorts later alphabetically wins, unless you set ansible_group_priority. Relying on that is fragile; avoid defining the same variable in sibling groups.

Dictionaries are replaced, not merged, by default: a host-level nginx: {port: 81} wipes the group-level nginx: {port: 80, workers: 4}. Prefer flat, prefixed variables (nginx_port), or merge explicitly with the combine filter.

Practical rules: put defaults in role defaults, environment and group values in group_vars, exceptions in host_vars, and use -e only for one-off overrides. To see the result, ansible-inventory --host web1 or a debug: var=... task.

Example

# roles/app/defaults/main.yml
app_port: 8080
app_settings: {workers: 4, log_level: info}

# inventory/group_vars/prod.yml
app_port: 80
app_settings: {log_level: warn}       # replaces the whole dict: workers is gone

# better: merge explicitly in the role
#   app_effective: "{{ app_settings_default | combine(app_settings_override | default({})) }}"

# ansible-inventory -i inventory --host web1 | grep -A3 app_
# ansible-playbook site.yml -e app_port=9000   # extra vars beat everything

Remember

  • Role defaults lowest, extra vars highest; host vars beat group vars
  • Sibling groups: later alphabetical wins unless ansible_group_priority
  • Dicts are replaced, not merged; prefer flat vars or combine

Watch out for

  • Overriding one key of a dictionary variable and silently losing all its other keys.

Check yourself

  1. A variable is set in role defaults, group_vars/all and host_vars/web1. Which value does web1 get?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.ansible.com/ansible/latest/playbook_guide/playbooks_variables.html#understanding-variable-precedence