Where a Role's Values Really Come From
Scenario: A team setswebapp_portingroup_vars/webservers.yml, but every host still serves the role's default of 8080. The role declared the variable invars/main.yml.
New words, in plain English
- Overridable - a value a consumer of the role can change from outside it.
- Effective constant - a value so high in precedence that no ordinary override reaches it.
- Role params - variables supplied at the call site under
- role: name.
This section exists because the defaults versus vars distinction causes more confusion than any other part of roles, and the symptom is always the same: an override that appears to be ignored.
Recall the precedence order. Role defaults are the weakest thing in Ansible - below inventory group vars, below host vars, below everything. Role vars are near the top - above group vars, above host vars, above play vars. The two directories differ by three letters and by nearly the entire precedence range.
So a value in vars/main.yml cannot be overridden from group_vars, from host_vars, or from a play's vars: block. Only role params at the call site or -e on the command line will beat it. If your role's users are reporting that configuration "does not work", this is the first thing to check.
The decision rule is simple: defaults for anything a user might reasonably change; vars for internal constants whose modification would break the role. A package name that differs per OS family belongs in vars/ (loaded via include_vars); a port number does not.
Analogy:defaultsis a suggested retail price - everyone expects to negotiate it.varsis the serial number stamped into the casing. Putting the price on the casing does not stop customers asking; it just stops you from ever answering.
A worked example
# roles/webapp/defaults/main.yml -> WEAKEST. Users can override freely.
webapp_port: 8080
webapp_workers: 4
# roles/webapp/vars/main.yml -> NEAR THE TOP. Effectively constant.
webapp_config_path: /etc/webapp/webapp.conf # changing this breaks the role
# group_vars/webservers.yml
webapp_port: 8090 # WINS over defaults -> 8090 applied
webapp_config_path: /tmp # LOSES to role vars -> ignored entirely
# The escape hatches that do beat role vars:
- role: webapp
webapp_config_path: /opt/webapp/webapp.conf # role param
ansible-playbook site.yml -e webapp_config_path=/opt/x # extra vars
There is a legitimate and common use of vars/ that is worth knowing: OS-conditional values loaded at run time. A role can keep vars/Debian.yml and vars/RedHat.yml with the right package and path names for each family, then load the correct one with include_vars: "{{ ansible_facts['os_family'] }}.yml". Those genuinely are internal constants for the platform, and their high precedence is appropriate.
When you are debugging an override that seems ignored, the fastest confirmation is a debug task placed inside the role: it prints the value the role will actually use, at the point it will use it, which settles the question in one run.
Tip: Symptom-to-cause shortcut: 'my group_vars override is being ignored by a role' almost always means the role declared that variable invars/instead ofdefaults/.
Goal: Put this to work in the ansible-variable-precedence lab. Open/labs/ansible, pickansible-variable-precedence, and fix the real broken project - Ansible really does SSH into four managed hosts and converge them.