Introduction

The inventory file tells Ansible which hosts to manage. It lists your servers, organizes them into groups, and assigns variables. You can write it in INI or YAML format, and it scales from a single server to thousands of hosts with dynamic inventory scripts.

INI Format

The most common format — simple and readable:

# /etc/ansible/hosts or ./inventory/hosts

# Ungrouped hosts
10.0.0.1
server1.example.com

# Web servers group
[webservers]
web-01 ansible_host=10.0.1.10
web-02 ansible_host=10.0.1.11
web-03 ansible_host=10.0.1.12

# Database servers group
[dbservers]
db-01 ansible_host=10.0.2.10
db-02 ansible_host=10.0.2.11

# Load balancers
[loadbalancers]
lb-01 ansible_host=10.0.0.5

YAML Format

More structured, supports complex data better:

# inventory/hosts.yml
all:
  children:
    webservers:
      hosts:
        web-01:
          ansible_host: 10.0.1.10
        web-02:
          ansible_host: 10.0.1.11
        web-03:
          ansible_host: 10.0.1.12
    dbservers:
      hosts:
        db-01:
          ansible_host: 10.0.2.10
        db-02:
          ansible_host: 10.0.2.11
    loadbalancers:
      hosts:
        lb-01:
          ansible_host: 10.0.0.5

Host Variables

# INI — variables on the host line
[webservers]
web-01 ansible_host=10.0.1.10 ansible_user=deploy http_port=80
web-02 ansible_host=10.0.1.11 ansible_user=deploy http_port=8080

# Common connection variables
[dbservers]
db-01 ansible_host=10.0.2.10 ansible_port=2222 ansible_user=dbadmin
# YAML — cleaner for many variables
all:
  children:
    webservers:
      hosts:
        web-01:
          ansible_host: 10.0.1.10
          ansible_user: deploy
          http_port: 80
        web-02:
          ansible_host: 10.0.1.11
          ansible_user: deploy
          http_port: 8080

Group Variables

# INI — group variables section
[webservers]
web-01
web-02

[webservers:vars]
ansible_user=deploy
http_port=80
document_root=/var/www/html

[dbservers]
db-01
db-02

[dbservers:vars]
ansible_user=dbadmin
db_port=5432
# YAML
all:
  children:
    webservers:
      vars:
        ansible_user: deploy
        http_port: 80
        document_root: /var/www/html
      hosts:
        web-01:
        web-02:

Groups of Groups

# Parent group containing other groups
[webservers]
web-01
web-02

[dbservers]
db-01

[monitoring]
grafana-01

# Group of groups (children)
[production:children]
webservers
dbservers

[infrastructure:children]
loadbalancers
monitoring

# Variables for the parent group
[production:vars]
env=production
# YAML equivalent
all:
  children:
    production:
      vars:
        env: production
      children:
        webservers:
          hosts:
            web-01:
            web-02:
        dbservers:
          hosts:
            db-01:
    infrastructure:
      children:
        loadbalancers:
          hosts:
            lb-01:
        monitoring:
          hosts:
            grafana-01:

Host Ranges

# Numeric ranges
[webservers]
web-[01:20]              # web-01, web-02, ... web-20

[dbservers]
db-[a:f].example.com     # db-a.example.com through db-f.example.com

# With step
[webservers]
web-[01:20:2]            # web-01, web-03, web-05, ... web-19 (odd only)

Common Connection Variables

VariableDescriptionExample
ansible_hostIP or hostname to connect to10.0.1.10
ansible_portSSH port2222
ansible_userSSH usernamedeploy
ansible_ssh_private_key_filePath to SSH key~/.ssh/deploy_key
ansible_python_interpreterPython path on remote/usr/bin/python3
ansible_connectionConnection typessh, local, winrm
ansible_becomeEnable privilege escalationtrue
ansible_become_userUser to becomeroot

Inventory Directory Structure

For larger setups, use a directory with separate files:

inventory/
├── hosts                  # Main inventory file
├── group_vars/
│   ├── all.yml           # Variables for ALL hosts
│   ├── webservers.yml    # Variables for webservers group
│   ├── dbservers.yml
│   └── production/
│       ├── vars.yml      # Plain vars
│       └── vault.yml     # Encrypted secrets
└── host_vars/
    ├── web-01.yml        # Variables for specific host
    └── db-01.yml
# Use the directory as inventory
ansible-playbook -i inventory/ site.yml

Multiple Inventories

# Different inventories per environment
ansible-playbook -i inventory/production site.yml
ansible-playbook -i inventory/staging site.yml

# Or in ansible.cfg
# [defaults]
# inventory = ./inventory/production

Host Patterns

Target specific hosts or groups in commands:

# All hosts
ansible all -m ping

# Specific group
ansible webservers -m ping

# Specific host
ansible web-01 -m ping

# Multiple groups (union)
ansible 'webservers:dbservers' -m ping

# Intersection (hosts in BOTH groups)
ansible 'webservers:&production' -m ping

# Exclude (webservers NOT in staging)
ansible 'webservers:!staging' -m ping

# Wildcard
ansible 'web-*' -m ping

# Regex
ansible '~web-0[1-3]' -m ping

# First host in group
ansible 'webservers[0]' -m ping

Dynamic Inventory

For cloud environments, generate inventory dynamically:

# AWS EC2
ansible-playbook -i aws_ec2.yml site.yml

# GCP
ansible-playbook -i gcp_compute.yml site.yml

aws_ec2.yml:

plugin: amazon.aws.aws_ec2
regions:
  - us-east-1
  - eu-west-1
keyed_groups:
  - key: tags.Environment
    prefix: env
  - key: instance_type
    prefix: type
filters:
  tag:Managed: ansible
  instance-state-name: running

Verify Your Inventory

# List all hosts
ansible-inventory -i inventory/ --list

# Show as graph
ansible-inventory -i inventory/ --graph

# Show specific host details
ansible-inventory -i inventory/ --host web-01

# Verify connectivity
ansible all -i inventory/ -m ping

Example graph output:

@all:
  |--@production:
  |  |--@webservers:
  |  |  |--web-01
  |  |  |--web-02
  |  |--@dbservers:
  |  |  |--db-01
  |--@ungrouped:
  |  |--bastion

Localhost

# Run tasks on the control node itself
[local]
localhost ansible_connection=local

Or in a playbook:

- name: Run locally
  hosts: localhost
  connection: local
  tasks:
    - ansible.builtin.debug:
        msg: "Running on control node"

Troubleshooting

"Could not match supplied host pattern"

The host or group doesn't exist in your inventory:

# Check what's in your inventory
ansible-inventory --list

# Verify the host exists
ansible-inventory --host web-01

Variables Not Loading from group_vars

Directory names must exactly match group names:

group_vars/webservers.yml    # ✅ matches [webservers]
group_vars/Webservers.yml    # ❌ case mismatch
group_vars/web-servers.yml   # ❌ name mismatch

Conclusion

The inventory file is the foundation of Ansible automation. Start with a simple INI file for small setups, move to YAML for complex variable structures, and use dynamic inventory for cloud environments. Organize variables in group_vars/ and host_vars/ directories to keep your inventory clean. Use ansible-inventory --graph to verify your host and group structure before running playbooks.