Organizing Variables by Inventory Structure
Instead of cramming every variable into the inventory file itself or a single play's vars: block, Ansible automatically loads variables from group_vars/<groupname>.yml (or a directory of that name) and host_vars/<hostname>.yml, placed alongside the inventory file or in the playbook's root directory. group_vars/all.yml applies to every host in the inventory regardless of group membership, while group_vars/webservers.yml applies only to hosts listed under the webservers group, giving you a clean, file-based way to separate broad defaults from environment- or role-specific overrides.
Cricket analogy: A cricket board issues a general code of conduct to every registered player (group_vars/all) but issues additional fast-bowling workload guidelines only to pace bowlers (group_vars/bowlers), keeping broad and specific rules in separate documents.
File and Directory Resolution
group_vars/webservers can be either a single file, group_vars/webservers.yml, or a directory, group_vars/webservers/, containing multiple files like vars.yml and vault.yml that Ansible merges together — this directory form is the recommended pattern specifically so that non-sensitive variables and vault-encrypted secrets can live in separate files while both apply to the same group. The exact same rule applies to host_vars/web01.yml versus a host_vars/web01/ directory, and Ansible discovers these files automatically based on their location relative to the inventory file, requiring no explicit include_vars statement in the playbook.
Cricket analogy: A team's dossier on an opponent can be one consolidated report or split into separate folders — batting analysis, bowling analysis, fielding weaknesses — that the coaching staff merges mentally, similar to group_vars as one file or a merged directory.
Group Nesting and Its Effect on Precedence
Inventory groups can nest via a children: key, so a group like datacenter_east can have webservers and dbservers as child groups, and a host belonging to webservers automatically inherits from both webservers and its ancestor datacenter_east's group_vars. When the same variable is set in two ancestor groups, the group_vars of the more specific (child) group wins over the more general (parent) group, and the special group_vars/all.yml always sits at the bottom, below every named group, since 'all' is the implicit parent of every other group in the inventory.
Cricket analogy: A regional cricket association's rule overrides a national board's general guideline for that specific region, but both sit below an ICC-wide rule that applies to no one specifically, mirroring how child group_vars override parent group_vars, with 'all' at the very bottom.
Organizing Variables at Scale
In a project managing dozens of environments, a common and battle-tested layout keeps group_vars/production/vars.yml and group_vars/production/vault.yml separate from group_vars/staging/vars.yml and group_vars/staging/vault.yml, each pair scoped to its own inventory group, so promoting a change from staging to production means editing one clearly-named file rather than hunting through a monolithic variables file for environment-specific lines. Combined with host_vars/ for the rare per-machine override (like a specific server's unique hostname-derived certificate path), this structure keeps large inventories navigable without sacrificing the ability to override any single value at exactly the right scope.
Cricket analogy: A national board keeps separate, clearly-labeled folders for its Test squad, ODI squad, and T20 squad selection criteria rather than one giant mixed document, similar to keeping group_vars/production and group_vars/staging as clearly separated directories.
inventory/
├── hosts.ini
├── group_vars/
│ ├── all.yml # applies to every host
│ ├── production/
│ │ ├── vars.yml # plaintext, references vault_ prefixed vars
│ │ └── vault.yml # ansible-vault encrypted secrets
│ └── staging/
│ ├── vars.yml
│ └── vault.yml
└── host_vars/
├── web01.yml # per-host override, rare
└── db01.yml
# inventory/hosts.ini
[webservers]
web01
web02
[dbservers]
db01
[datacenter_east:children]
webservers
dbservers
# inventory/group_vars/production/vars.yml
environment_name: production
http_port: 443
db_password: "{{ vault_db_password }}"Use the group_vars/<group>/ directory form (not a single .yml file) as soon as a group needs both plaintext and vault-encrypted variables, so ansible-vault edit only ever touches the vault.yml file and vars.yml stays diff-friendly in code review.
Group names in group_vars/ and host_vars/ file names must exactly match the group or host name in your inventory, including case — group_vars/WebServers.yml will not apply to a group named webservers in most environments, since inventory group name matching is case-sensitive by default.
- group_vars/<group>.yml or group_vars/<group>/ applies variables to every host in that inventory group.
- host_vars/<hostname>.yml or host_vars/<hostname>/ applies variables to exactly one host.
- group_vars/all.yml applies to every host in the inventory and sits at the bottom of group precedence.
- The directory form (group_vars/<group>/vars.yml plus vault.yml) is recommended for separating plaintext from encrypted secrets.
- Nested groups via children: mean a host inherits from every ancestor group, with more specific child groups winning.
- Ansible auto-discovers these files by location relative to the inventory; no include_vars statement is required.
- Group and host names in file names must exactly match inventory names, including case sensitivity.
Practice what you learned
1. Which file applies a variable to every single host in the inventory, regardless of group membership?
2. Why is the group_vars/<group>/ directory form (versus a single .yml file) recommended for groups with secrets?
3. If a host belongs to a child group nested under a parent group via children:, and both define the same variable, which value applies?
4. What happens if a group_vars file is named group_vars/WebServers.yml but the inventory group is named webservers (lowercase)?
5. Do you need an explicit include_vars task to load group_vars and host_vars files?
Was this page helpful?
You May Also Like
Variables and Precedence
How Ansible collects variable values from more than twenty possible sources and resolves conflicts using a strict, well-defined precedence order.
Vault and Secrets Management
How Ansible Vault encrypts sensitive data at rest, how vault IDs support multiple passwords, and how to integrate vault into automated pipelines safely.
Facts and Gathering Facts
How Ansible discovers system information automatically with the setup module, how to extend it with custom facts, and how fact caching speeds up large runs.
Related Reading
Related Study Notes in DevOps
Browse all study notesNginx Study Notes
DevOps · 30 topics
DevOpsAdvanced Kubernetes Study Notes
Kubernetes · 30 topics
DevOpsAdvanced Bash Scripting Study Notes
Bash · 30 topics
DevOpsApache Kafka Study Notes
Kafka · 30 topics
DevOpsDocker & Kubernetes Study Notes
YAML · 40 topics
DevOpsCI/CD Tools & Pipelines Study Notes
YAML · 37 topics