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=2222
YAML 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=production
Separate 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: auto
Connection 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 |
See also: Automate Text Capitalization with Ansible Playbooks
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 hosts
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_address
Azure
# azure_rm.yml
plugin: azure.azcollection.azure_rm
auth_source: auto
include_vm_resource_groups:
- myResourceGroup
keyed_groups:
- prefix: tag
key: tags
Custom 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.yml
Verify 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 web1
Best Practices
Use YAML format — more readable and supports complex structures Usegroup_vars/ and host_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=80
Host Variables
# host_vars/web1.yml
http_port: 8080
ssl_enabled: true
custom_domains:
- example.com
- www.example.com
Group 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.com
Patterns (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 ping
Ranges
webservers:
hosts:
web[1:10]:
ansible_host: 192.168.1.[10:19]
# Creates web1 through web10
Dynamic Inventory
# aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions: [us-east-1]
keyed_groups:
- key: tags.Environment
prefix: env
filters:
instance-state-name: running
ansible-inventory -i aws_ec2.yml --list
ansible -i aws_ec2.yml env_production -m ping
Multiple Inventory Sources
ansible-playbook -i inventory/production -i inventory/staging site.yml
# ansible.cfg
[defaults]
inventory = inventory/production,inventory/staging
Verify 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 web1
Special 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 ping
Can 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
dbservers
YAML 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=5986
Patterns
# 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 ping
Ranges
[webservers]
web[01:50].example.com # web01 through web50
db-[a:f].example.com # db-a through db-f
group_vars / host_vars
inventory/
├── hosts
├── group_vars/
│ ├── all.yml
│ ├── webservers.yml
│ └── production.yml
└── host_vars/
├── web1.yml
└── db1.yml
Dynamic 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_address
ansible-inventory -i aws_ec2.yml --graph
Multiple 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/production
Verify Inventory
# List all hosts
ansible-inventory --list
# Show graph
ansible-inventory --graph
# Show host variables
ansible-inventory --host web1
FAQ
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/hosts
INI 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
dbservers
YAML 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: deploy
Host 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 range
Group Variables
inventory/
├── hosts.yml
├── group_vars/
│ ├── all.yml
│ ├── webservers.yml
│ └── production.yml
└── host_vars/
├── web1.yml
└── db1.yml
Dynamic 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_address
Multi-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.yml
List and Debug
ansible-inventory -i hosts.yml --list
ansible-inventory -i hosts.yml --graph
ansible all -i hosts.yml -m ping
Implicit 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/dynamic
Variable precedence in inventory?
host_vars > group_vars (child group) > group_vars (parent group) > group_vars/all.
Related Articles
• Ansible Vault best practices • Nginx hardening via Ansible • privilege escalation with Ansible become • Route53 with AnsibleCategory: windows-automation