Introduction
The inventory is Ansible's source of truth — it defines which hosts to manage, how to group them, and what variables apply. Whether you use a simple INI file with 5 servers or dynamic inventory pulling thousands of hosts from AWS, understanding inventory is fundamental to every Ansible workflow.
INI Format (Simple)
# inventory.ini
[webservers]
web1.example.com
web2.example.com
web3.example.com
[dbservers]
db1.example.com
db2.example.com
[loadbalancers]
lb1.example.com ansible_host=192.168.1.10
# Group of groups
[production:children]
webservers
dbservers
loadbalancers
# Group variables
[webservers:vars]
http_port=80
ansible_user=deploy
YAML Format (Recommended)
# inventory.yml
all:
children:
webservers:
hosts:
web1.example.com:
web2.example.com:
web3.example.com:
vars:
http_port: 80
ansible_user: deploy
dbservers:
hosts:
db1.example.com:
postgresql_port: 5432
db2.example.com:
postgresql_port: 5433
loadbalancers:
hosts:
lb1.example.com:
ansible_host: 192.168.1.10
production:
children:
webservers:
dbservers:
loadbalancers:
Host and Group Variables
Inline Variables
all:
hosts:
web1.example.com:
ansible_host: 192.168.1.101
ansible_port: 2222
ansible_user: admin
ansible_python_interpreter: /usr/bin/python3
Variable Files (Best Practice)
inventory/
├── hosts.yml
├── group_vars/
│ ├── all.yml # Applied to all hosts
│ ├── webservers.yml # Applied to webservers group
│ └── production.yml # Applied to production group
└── host_vars/
├── web1.example.com.yml
└── db1.example.com.yml
# group_vars/webservers.yml
http_port: 80
ssl_cert_path: /etc/ssl/certs/site.pem
max_connections: 1000
deploy_user: www-data
# host_vars/db1.example.com.yml
postgresql_version: "16"
postgresql_max_connections: 200
backup_schedule: "0 2 * * *"
Variable Precedence
1. command line -e (highest)
2. host_vars/hostname.yml
3. group_vars/child_group.yml
4. group_vars/parent_group.yml
5. group_vars/all.yml (lowest)
Host Patterns
# All hosts
ansible all -m ping
# Specific group
ansible webservers -m ping
# Single host
ansible web1.example.com -m ping
# Multiple groups (union)
ansible 'webservers:dbservers' -m ping
# Intersection
ansible 'webservers:&production' -m ping
# Exclusion
ansible 'all:!loadbalancers' -m ping
# Wildcard
ansible 'web*' -m ping
# Regex
ansible '~web[0-9]+\.example\.com' -m ping
# Numeric range
ansible 'web[1:3].example.com' -m ping
# Matches web1, web2, web3
# Limit at runtime
ansible-playbook site.yml --limit web1.example.com
ansible-playbook site.yml --limit 'webservers:!web3.example.com'
Dynamic Inventory
AWS EC2
# aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions:
- us-east-1
- eu-west-1
filters:
tag:Environment:
- production
instance-state-name: running
keyed_groups:
- key: tags.Role
prefix: role
- key: placement.region
prefix: region
- key: instance_type
prefix: type
compose:
ansible_host: public_ip_address
instance_name: tags.Name
# Use dynamic inventory
ansible-inventory -i aws_ec2.yml --graph
ansible-playbook -i aws_ec2.yml site.yml
Azure
# azure_rm.yml
plugin: azure.azcollection.azure_rm
auth_source: auto
include_vm_resource_groups:
- production-rg
- staging-rg
keyed_groups:
- prefix: tag
key: tags
GCP
# gcp_compute.yml
plugin: google.cloud.gcp_compute
projects:
- my-project-id
zones:
- us-central1-a
filters:
- status = RUNNING
keyed_groups:
- key: labels.env
prefix: env
ServiceNow CMDB
# snow_inventory.yml
plugin: servicenow.itsm.now
table: cmdb_ci_linux_server
query:
- operational_status: = operational
compose:
ansible_host: ip_address
Multiple Inventory Sources
# Use multiple inventory files
ansible-playbook -i inventory/production -i inventory/staging site.yml
# Or use a directory
ansible-playbook -i inventory/ site.yml
# ansible.cfg
[defaults]
inventory = inventory/production,inventory/cloud
Multi-Environment Setup
inventory/
├── production/
│ ├── hosts.yml
│ ├── group_vars/
│ │ └── all.yml
│ └── host_vars/
├── staging/
│ ├── hosts.yml
│ ├── group_vars/
│ │ └── all.yml
│ └── host_vars/
└── development/
├── hosts.yml
└── group_vars/
└── all.yml
# Deploy to staging
ansible-playbook -i inventory/staging site.yml
# Deploy to production
ansible-playbook -i inventory/production site.yml
Inventory Plugins
# Combine static and dynamic
# inventory/combined.yml
plugin: ansible.builtin.constructed
strict: false
groups:
webservers: "'web' in inventory_hostname"
large_instances: "ansible_memtotal_mb > 16000"
compose:
ansible_connection: "'local' if inventory_hostname == 'localhost' else 'ssh'"
Verify Inventory
# List all hosts
ansible-inventory -i inventory.yml --list
# Show graph
ansible-inventory -i inventory.yml --graph
# Show specific host variables
ansible-inventory -i inventory.yml --host web1.example.com
# Verify connectivity
ansible all -i inventory.yml -m ping
Inventory Best Practices
# ✅ Use YAML format
# ✅ Use group_vars/ and host_vars/ directories
# ✅ Use meaningful group names
# ✅ Encrypt sensitive vars with Ansible Vault
# ✅ Use dynamic inventory for cloud
# ✅ Keep inventory in version control
# ❌ Don't put passwords in inventory files
# ❌ Don't use IP addresses as hostnames (use DNS)
# ❌ Don't create groups with single hosts (unless meaningful)
# ❌ Don't duplicate variables across groups
Complete Example
# inventory/production/hosts.yml
all:
children:
webservers:
hosts:
web-[01:03].prod.example.com:
dbservers:
hosts:
db-01.prod.example.com:
role: primary
db-02.prod.example.com:
role: replica
monitoring:
hosts:
mon-01.prod.example.com:
production:
children:
webservers:
dbservers:
monitoring:
vars:
env: production
ansible_user: deploy
ansible_become: true
ntp_server: time.prod.example.com
Related Articles
Conclusion
Ansible inventory ranges from simple INI files to multi-cloud dynamic discovery. Master these patterns: use YAML format for readability, group_vars/ and host_vars/ directories for clean variable management, dynamic inventory plugins for cloud environments, and host patterns for flexible targeting. For large environments, combine static and dynamic sources, separate by environment, and use the constructed plugin for computed groups. The inventory is the foundation of everything Ansible does — invest time getting it right.