Ansible Inventory: Complete Guide to INI, YAML & Dynamic Inventory
By Luca Berton · Published 2026-04-03 · Category: windows-automation
Complete guide to Ansible inventory. Create INI and YAML inventories, use groups, variables, dynamic inventory plugins, and manage multi-environment setups.
The Ansible inventory defines the hosts and groups of hosts that Ansible manages. It's the foundation of every Ansible deployment — without it, Ansible doesn't know what to automate.
What is an Ansible Inventory?
An inventory is a file (or script, or plugin) that lists your managed hosts. It can be as simple as a flat list of hostnames or as complex as a dynamic cloud inventory pulling from AWS, Azure, or GCP.
Default location: /etc/ansible/hosts
Custom location: ansible-playbook -i inventory.yml site.yml
See also: Installing Ansible: A Step-by-Step Guide
INI Format (Traditional)
# Simple host list
web1.example.com
web2.example.com
db1.example.com
# Groups
[webservers]
web1.example.com
web2.example.com
[databases]
db1.example.com
db2.example.com
# Group of groups
[production:children]
webservers
databases
# Group variables
[webservers:vars]
http_port=80
ansible_user=deploy
# Host variables
web1.example.com ansible_host=192.168.1.10 ansible_port=2222YAML Format (Recommended)
all:
children:
webservers:
hosts:
web1.example.com:
ansible_host: 192.168.1.10
http_port: 80
web2.example.com:
ansible_host: 192.168.1.11
http_port: 8080
vars:
ansible_user: deploy
ansible_become: true
databases:
hosts:
db1.example.com:
ansible_host: 192.168.1.20
db2.example.com:
ansible_host: 192.168.1.21
vars:
ansible_user: dbadmin
production:
children:
webservers:
databases:See also: Ansible Inventory: Static, Dynamic & Advanced Patterns (Complete Guide)
Host and Group Variables
Inline variables
[webservers]
web1 ansible_host=10.0.1.1 ansible_user=ubuntu app_env=productionSeparate variable files (recommended)
inventory/
├── hosts.yml
├── group_vars/
│ ├── all.yml # Variables for all hosts
│ ├── webservers.yml # Variables for webservers group
│ └── databases.yml # Variables for databases group
└── host_vars/
├── web1.yml # Variables for web1 only
└── db1.yml # Variables for db1 only# group_vars/webservers.yml
ansible_user: deploy
http_port: 80
app_version: "2.5.1"
nginx_worker_processes: autoConnection Variables
| Variable | Description | Example |
|---|---|---|
ansible_host | IP or hostname to connect to | 192.168.1.10 |
ansible_port | SSH port | 2222 |
ansible_user | SSH username | deploy |
ansible_ssh_private_key_file | SSH key path | ~/.ssh/deploy_key |
ansible_connection | Connection type | ssh, winrm, local |
ansible_become | Enable privilege escalation | true |
ansible_python_interpreter | Python path on remote | /usr/bin/python3 |
Inventory Patterns
# All hosts
ansible all -m ping
# Specific group
ansible webservers -m ping
# Multiple groups (OR)
ansible 'webservers:databases' -m ping
# Intersection (AND)
ansible 'webservers:&production' -m ping
# Exclusion (NOT)
ansible 'all:!databases' -m ping
# Wildcards
ansible 'web*' -m ping
# Regex
ansible '~web[0-9]+\.example\.com' -m ping
# Index
ansible 'webservers[0]' -m ping # First host
ansible 'webservers[0:2]' -m ping # First three hostsSee also: Automate Text Capitalization with Ansible Playbooks
Dynamic Inventory
AWS EC2
# aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions:
- us-east-1
- eu-west-1
filters:
tag:Environment: production
keyed_groups:
- key: tags.Role
prefix: role
- key: placement.region
prefix: aws_region
compose:
ansible_host: private_ip_addressAzure
# azure_rm.yml
plugin: azure.azcollection.azure_rm
auth_source: auto
include_vm_resource_groups:
- myResourceGroup
keyed_groups:
- prefix: tag
key: tagsCustom Script
#!/usr/bin/env python3
import json
inventory = {
"webservers": {
"hosts": ["web1.example.com", "web2.example.com"],
"vars": {"http_port": 80}
},
"_meta": {
"hostvars": {
"web1.example.com": {"ansible_host": "10.0.1.1"},
"web2.example.com": {"ansible_host": "10.0.1.2"}
}
}
}
print(json.dumps(inventory))Multiple Inventory Sources
# Pass multiple inventory files
ansible-playbook -i staging -i production site.yml
# Use a directory
ansible-playbook -i inventory/ site.ymlVerify Your Inventory
# List all hosts
ansible-inventory -i inventory.yml --list
# Show as graph
ansible-inventory -i inventory.yml --graph
# Show specific host vars
ansible-inventory -i inventory.yml --host web1Best Practices
- Use YAML format — more readable and supports complex structures
- Use
group_vars/andhost_vars/— separate variables from host definitions - Keep sensitive data in Ansible Vault — encrypt passwords and keys
- Use meaningful group names — reflect function, not location
- Layer groups —
[production:children]for logical grouping - Use dynamic inventory for cloud — static files become outdated
- Version control your inventory — track changes over time
FAQ
Where does Ansible look for the inventory file?
By default: /etc/ansible/hosts. Override with -i flag or inventory in ansible.cfg.
Can I use both static and dynamic inventory?
Yes. Put both files in an inventory directory and pass the directory with -i inventory/.
What's the difference between ansible_host and the hostname in inventory?
The inventory name is a label. ansible_host is the actual IP/hostname Ansible connects to. They can be different.
How do I test if my inventory is working?
Run: ansible all -i inventory.yml -m ping
YAML Inventory
# inventory.yml
all:
children:
webservers:
hosts:
web1:
ansible_host: 192.168.1.10
web2:
ansible_host: 192.168.1.11
vars:
http_port: 80
dbservers:
hosts:
db1:
ansible_host: 192.168.1.20
ansible_user: postgres
production:
children:
webservers:
dbservers:INI Inventory
[webservers]
web1 ansible_host=192.168.1.10
web2 ansible_host=192.168.1.11
[dbservers]
db1 ansible_host=192.168.1.20
[production:children]
webservers
dbservers
[webservers:vars]
http_port=80Host Variables
# host_vars/web1.yml
http_port: 8080
ssl_enabled: true
custom_domains:
- example.com
- www.example.comGroup Variables
# group_vars/webservers.yml
ansible_user: deploy
http_port: 80
document_root: /var/www/html
# group_vars/all.yml (applies to everything)
ansible_python_interpreter: /usr/bin/python3
ntp_server: time.google.comPatterns (Target Hosts)
# All hosts
ansible all -m ping
# Specific group
ansible webservers -m ping
# Multiple groups
ansible 'webservers:dbservers' -m ping
# Intersection (in BOTH groups)
ansible 'webservers:&production' -m ping
# Exclusion
ansible 'all:!dbservers' -m ping
# Specific host
ansible web1 -m ping
# Wildcard
ansible 'web*' -m ping
# Regex
ansible '~web[0-9]+' -m pingRanges
webservers:
hosts:
web[1:10]:
ansible_host: 192.168.1.[10:19]
# Creates web1 through web10Dynamic Inventory
# aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions: [us-east-1]
keyed_groups:
- key: tags.Environment
prefix: env
filters:
instance-state-name: runningansible-inventory -i aws_ec2.yml --list
ansible -i aws_ec2.yml env_production -m pingMultiple Inventory Sources
ansible-playbook -i inventory/production -i inventory/staging site.yml# ansible.cfg
[defaults]
inventory = inventory/production,inventory/stagingVerify Inventory
# List all hosts
ansible-inventory -i inventory.yml --list
# Show host graph
ansible-inventory -i inventory.yml --graph
# Show specific host vars
ansible-inventory -i inventory.yml --host web1Special Variables
| Variable | Description |
|---|---|
inventory_hostname | Host name as in inventory |
ansible_host | Connection address |
ansible_port | SSH port (default 22) |
ansible_user | SSH user |
ansible_connection | Connection type (ssh/local/winrm) |
ansible_python_interpreter | Python path on remote |
groups | Dict of all groups |
group_names | Groups current host belongs to |
FAQ
YAML vs INI — which should I use?
YAML is recommended. It handles complex nested structures, lists, and dictionaries better than INI format.
How do I test inventory without running a playbook?
ansible-inventory --list -i inventory.yml
ansible all -i inventory.yml -m pingCan I combine static and dynamic inventory?
Yes — point to a directory containing both static files and dynamic inventory scripts/plugins.
INI Format
# inventory/hosts
[webservers]
web1 ansible_host=10.0.1.10
web2 ansible_host=10.0.1.11
[dbservers]
db1 ansible_host=10.0.2.10
[webservers:vars]
http_port=80
ansible_user=deploy
[production:children]
webservers
dbserversYAML Format
# inventory/hosts.yml
all:
children:
webservers:
hosts:
web1:
ansible_host: 10.0.1.10
web2:
ansible_host: 10.0.1.11
vars:
http_port: 80
dbservers:
hosts:
db1:
ansible_host: 10.0.2.10
production:
children:
webservers:
dbservers:Common Variables
[all:vars]
ansible_user=deploy
ansible_ssh_private_key_file=~/.ssh/deploy_key
ansible_python_interpreter=/usr/bin/python3
[windows:vars]
ansible_connection=winrm
ansible_winrm_transport=ntlm
ansible_port=5986Patterns
# All hosts
ansible all -m ping
# Specific group
ansible webservers -m ping
# Multiple groups
ansible 'webservers:dbservers' -m ping
# Intersection
ansible 'webservers:&production' -m ping
# Exclusion
ansible 'all:!dbservers' -m ping
# Wildcard
ansible 'web*' -m ping
# Regex
ansible '~web[0-9]+' -m pingRanges
[webservers]
web[01:50].example.com # web01 through web50
db-[a:f].example.com # db-a through db-fgroup_vars / host_vars
inventory/
├── hosts
├── group_vars/
│ ├── all.yml
│ ├── webservers.yml
│ └── production.yml
└── host_vars/
├── web1.yml
└── db1.ymlDynamic Inventory (AWS)
# aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions: [us-east-1]
filters:
tag:Environment: production
keyed_groups:
- key: tags.Role
prefix: role
compose:
ansible_host: public_ip_addressansible-inventory -i aws_ec2.yml --graphMultiple Inventories
# Use multiple inventory sources
ansible-playbook -i inventory/staging -i inventory/production site.yml
# Or set in ansible.cfg
# [defaults]
# inventory = inventory/staging,inventory/productionVerify Inventory
# List all hosts
ansible-inventory --list
# Show graph
ansible-inventory --graph
# Show host variables
ansible-inventory --host web1FAQ
INI vs YAML format?
INI is simpler for small inventories. YAML is better for complex, nested structures. Both work identically.
Can I mix static and dynamic inventory?
Yes — put both in the same directory and Ansible merges them.
How to test connectivity?
ansible all -m ping -i inventory/hostsINI Format
[webservers]
web1 ansible_host=192.168.1.10
web2 ansible_host=192.168.1.11
[dbservers]
db1 ansible_host=192.168.1.20
[all:vars]
ansible_user=deploy
ansible_python_interpreter=/usr/bin/python3
[production:children]
webservers
dbserversYAML Format
all:
children:
webservers:
hosts:
web1:
ansible_host: 192.168.1.10
web2:
ansible_host: 192.168.1.11
dbservers:
hosts:
db1:
ansible_host: 192.168.1.20
vars:
ansible_user: deployHost Ranges
[webservers]
web[1:10].example.com # web1 through web10
db-[a:c].example.com # db-a, db-b, db-c
192.168.1.[10:20] # IP rangeGroup Variables
inventory/
├── hosts.yml
├── group_vars/
│ ├── all.yml
│ ├── webservers.yml
│ └── production.yml
└── host_vars/
├── web1.yml
└── db1.ymlDynamic Inventory (AWS)
# aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions:
- us-east-1
keyed_groups:
- key: tags.Environment
prefix: env
filters:
instance-state-name: running
compose:
ansible_host: public_ip_addressMulti-Environment
inventory/
├── production/
│ ├── hosts.yml
│ └── group_vars/
└── staging/
├── hosts.yml
└── group_vars/ansible-playbook -i inventory/production site.yml
ansible-playbook -i inventory/staging site.ymlList and Debug
ansible-inventory -i hosts.yml --list
ansible-inventory -i hosts.yml --graph
ansible all -i hosts.yml -m pingImplicit localhost
# localhost is always available
- hosts: localhost
connection: local
tasks:
- debug: msg="Running locally"FAQ
INI vs YAML format?
YAML is more flexible for complex structures. INI is simpler for small setups. Both work equally well.
How to use multiple inventory sources?
ansible-playbook -i inv1.yml -i inv2.yml site.yml
# Or set in ansible.cfg
# inventory = inventory/static,inventory/dynamicVariable precedence in inventory?
host_vars > group_vars (child group) > group_vars (parent group) > group_vars/all.
Related Articles
Category: windows-automation