Ansible OEL 8 DevOps · OEL 8 · Fundamentals

AnsibleVariables — Precedence and Scoping

Where to declare variables, how Ansible resolves the same variable defined in five places at once, and which level to use for which kind of value — defaults, secrets, environment overrides.

Ansible accepts variables from at least 22 different sources — role defaults, group_vars, host_vars, play vars, task vars, registered facts, the CLI, and so on. The crucial thing is the precedence order — when the same variable name is defined in multiple places, which value wins?

↑ Wins (overrides everything below) extra-vars (-e) highest priority — CLI override task vars (vars: in task) scoped to that one task block vars scoped to a block of tasks role vars (roles//vars/main.yml) fixed role configuration set_fact / register runtime-defined play vars (vars: in play) scoped to the play inventory vars (group_vars/host_vars) shared across runs role defaults lowest — easiest to override ↓ Most easily overridden — sensible defaults
LevelWhereUse it for
1 — extra-vars (highest)-e var=value on the CLIOne-off overrides, CI parameters
2 — task varsvars: on a single taskLoop iteration variables
3 — block varsvars: on a block:Shared values for a few tasks
4 — role varsroles//vars/main.ymlRole-internal "constants"
5 — set_fact / registerRuntime, in tasksComputed values from earlier tasks
6 — play varsvars: on the playMost app-level config
7 — inventory varsgroup_vars/ + host_vars/Per-environment overrides
8 — role defaults (lowest)roles//defaults/main.ymlSensible defaults to be overridden
💡 Tip: Rule of thumb: put sensible defaults in defaults/main.yml (lowest priority — easy to override), put per-environment overrides in group_vars/<env>.yml, and keep secrets in Ansible Vault files referenced from group_vars.
YAML — variable definitions across levels
# 1. CLI extra-vars (wins everything)
# ansible-playbook site.yml -e "mysql_port=3307"

# 2. group_vars/databases.yml
mysql_port: 3306
mysql_root_password: "{{{{ vault_mysql_root_password }}}}"

# 3. host_vars/db1.example.com.yml — overrides group_vars for this one host
server_id: 1
mysql_role: primary

# 4. play-level vars: in a playbook
- hosts: databases
  vars:
    backup_retention_days: 7
  tasks: ...

# 5. role defaults — roles/mysql/defaults/main.yml
mysql_max_connections: 200
mysql_innodb_buffer_pool_size: "1G"
YAML — using variables
- hosts: databases
  vars:
    mysql_port: 3306
    mysql_data_dir: /var/lib/mysql

  tasks:
    # Inside a string — wrap in {{{{ }}}}, always quote the whole string
    - name: Render my.cnf
      ansible.builtin.template:
        src: my.cnf.j2
        dest: "/etc/my.cnf"
      vars:
        mysql_pidfile: "{{{{ mysql_data_dir }}}}/mysqld.pid"

    # Inside a module argument — same syntax
    - name: Open MySQL port
      ansible.posix.firewalld:
        port: "{{{{ mysql_port }}}}/tcp"
        state: enabled

    # Bare expression (when the WHOLE value is the variable)
    - name: Set log path
      ansible.builtin.set_fact:
        log_path: "/var/log/mysql/{{{{ inventory_hostname }}}}.log"

Ansible exposes built-in variables with information about the run:

VariableWhat it holds
inventory_hostnameThe name of the current host as it appears in inventory
ansible_hostThe actual address used for SSH (may differ from inventory name)
groupsDictionary of all groups → list of hosts
group_namesList of groups the current host belongs to
hostvarsDict of all hosts → their variables
play_hostsList of hosts in the current play
ansible_factsAll gathered facts (OS, network, hardware…)
ansible_date_timeDate / time facts from the gathering pass
BASH — debug what a variable will be
# Show all variables for a host (huge output, pipe to less)
ansible db1 -m ansible.builtin.debug -a "var=hostvars[inventory_hostname]"

# Show one specific variable
ansible db1 -m ansible.builtin.debug -a "var=mysql_port"

# Useful at the start of a play to see what came in
- name: Dump all play vars
  ansible.builtin.debug:
    var: vars
⚠ Warning: Avoid changing variable values mid-play with set_fact if a downstream task in another play needs the original. Use set_fact for new variables, not for mutating shared inputs.