Ansible OEL 8 DevOps · OEL 8 · Fundamentals

Ansiblegroup_vars and host_vars

The directory layout that Ansible auto-loads to apply per-environment, per-group, and per-host variables — without having to reference any vars file by name.

When you point Ansible at an inventory directory, it automatically picks up two companion folders next to the inventory file: group_vars/ and host_vars/. The filename inside those folders matches a group name or host name from the inventory.

production/ an inventory ├── hosts.ini — defines [groups] and host IPs ├── group_vars/ │ ├── all.yml — vars for every host in inventory │ ├── databases.yml — vars for any host in [databases] │ ├── mysql.yml — vars for [mysql] (sub-group) │ └── webservers.yml — vars for [webservers] └── host_vars/ ├── db1.example.com.yml — vars for ONE host (overrides group_vars) ├── db2.example.com.yml └── web1.example.com.yml host_vars beat group_vars · sub-group vars beat parent group vars
  1. For every host in inventory, Ansible loads group_vars/all.yml first.
  2. Then it loads group_vars/<group>.yml for every group the host belongs to.
  3. Sub-group variables override parent-group variables.
  4. Finally host_vars/<hostname>.yml wins over any group_vars.
DIR — production inventory
production/
├── hosts.ini
├── group_vars/
│   ├── all.yml                 # applies to every host
│   ├── databases.yml           # applies to all DB hosts
│   ├── mysql.yml               # applies to MySQL hosts only
│   ├── mysql_primary.yml       # applies to mysql_primary group
│   ├── mysql_replica.yml       # applies to mysql_replica group
│   └── webservers.yml          # applies to web hosts
└── host_vars/
    ├── db1.example.com.yml     # overrides for db1
    ├── db2.example.com.yml     # overrides for db2
    └── web1.example.com.yml
INI — production/hosts.ini
[mysql_primary]
db1.example.com

[mysql_replica]
db2.example.com
db3.example.com

[mysql:children]
mysql_primary
mysql_replica

[databases:children]
mysql

[webservers]
web1.example.com
web2.example.com

[all:vars]
ansible_user=deploy
YAML — group_vars/all.yml
---
# Defaults that apply to every host across the entire inventory
ansible_python_interpreter: /usr/bin/python3
ntp_servers:
  - 0.pool.ntp.org
  - 1.pool.ntp.org
timezone: UTC
admin_users:
  - alice
  - bob
YAML — group_vars/databases.yml
---
# Applied to every host in [databases] (i.e. all MySQL hosts via the children chain)
backup_retention_days: 14
firewall_open_ports:
  - 22
  - 3306
db_admin_email: dba@example.com
YAML — group_vars/mysql_primary.yml
---
# Sub-group — applied only to db1.example.com
mysql_role: primary
server_id: 1
mysql_log_bin: true
mysql_max_connections: 500
YAML — host_vars/db1.example.com.yml
---
# Per-host overrides — beat all group_vars
mysql_innodb_buffer_pool_size: "8G"   # bigger box, more buffer pool
custom_motd: "PRIMARY DB · do not run heavy queries here"

For larger inventories, replace a single file with a directory of the same name — Ansible loads every .yml inside it (alphabetical order):

DIR — group_vars as directories
group_vars/
├── databases/
│   ├── connection.yml      # connection settings
│   ├── tuning.yml          # buffer pools, max_connections, etc.
│   └── vault.yml           # encrypted secrets (Ansible Vault)
└── webservers/
    ├── nginx.yml
    └── tls.yml
💡 Tip: This pattern is great for separating plain config from secrets: keep vault.yml encrypted with Ansible Vault, leave the others readable. Both files are auto-loaded.

The convention for multiple environments is to put each one in its own folder:

DIR — multi-environment
inventories/
├── dev/
│   ├── hosts.ini
│   ├── group_vars/
│   │   └── databases.yml
│   └── host_vars/
├── staging/
│   ├── hosts.ini
│   ├── group_vars/
│   │   └── databases.yml
│   └── host_vars/
└── production/
    ├── hosts.ini
    ├── group_vars/
    │   └── databases.yml
    └── host_vars/

# Run against an environment by pointing -i at its directory
ansible-playbook -i inventories/staging site.yml
ansible-playbook -i inventories/production site.yml
✅ Tip: Same playbook, three environments, zero per-environment branching in the playbook itself — variables alone differentiate them. This is the Ansible way.