Ansible — Inventory File

A structured YAML inventory file that defines three host groups — webservers, dbservers, and monitoring — with global connection defaults, per-group variables, and per-host overrides for connection settings and application-specific configuration.

🛠 Paste this YAML into the validator to check it instantly.

Open Validator →

Overview

An Ansible inventory defines the hosts and groups that Ansible manages. While the original INI-style inventory format is still supported, the YAML format is more expressive: it supports nested group hierarchies, inline variables, and is easier to read and validate. Use the inventory by passing it with the -i flag: ansible-playbook -i inventory.yml nginx.yml.

The YAML inventory structure always starts with all, the implicit parent group that contains every host. Under all, you define global variables in all.vars and sub-groups in all.children. Each group can have its own hosts, vars, and children — allowing arbitrarily deep nesting for complex infrastructure hierarchies. Variables follow a clear inheritance order: host variables beat group variables, which beat parent group variables, which beat all.vars.

Test your inventory with ansible-inventory -i inventory.yml --list to see the fully-resolved host and variable tree as JSON, or with --graph for a visual hierarchy. Use ansible all -i inventory.yml -m ping to verify connectivity to all hosts.

Full YAML Copy-paste ready

inventory.yml
all:
  vars:
    ansible_user: ubuntu
    ansible_ssh_private_key_file: ~/.ssh/id_rsa
    ansible_python_interpreter: /usr/bin/python3

  children:
    webservers:
      hosts:
        web1:
          ansible_host: 10.0.1.10
        web2:
          ansible_host: 10.0.1.11
      vars:
        nginx_port: 80
        document_root: /var/www/html

    dbservers:
      hosts:
        db1:
          ansible_host: 10.0.2.10
          ansible_port: 22
          ansible_user: admin
      vars:
        postgres_version: "16"
        postgres_data_dir: /var/lib/postgresql/16/main

    monitoring:
      hosts:
        monitor1:
          ansible_host: 10.0.3.10

Key sections explained

all.vars for global defaults

Variables defined under all.vars apply to every host in the inventory. These are the lowest-priority variables, so any host-level or group-level variable with the same name will override them. This is the right place for connection defaults that apply across your entire infrastructure: the SSH user (ansible_user: ubuntu), the SSH key (ansible_ssh_private_key_file), and the Python interpreter path. Setting these globally avoids repeating them for every group and host.

Connection variables that start with ansible_ are special: Ansible reads them automatically to control how it connects to each host. The full list includes ansible_host, ansible_port, ansible_user, ansible_password, ansible_ssh_private_key_file, ansible_connection, and many more. Non-prefixed variables like nginx_port are user-defined and are only meaningful in your playbook tasks and templates.

children for group nesting

The children key under a group defines sub-groups. Here, webservers, dbservers, and monitoring are all children of all. You can nest groups further — for example, you could have a production group that contains webservers and dbservers as children, and a staging group with its own sub-groups. A host can belong to multiple groups simultaneously, and it will inherit variables from all of them (with the lowest-priority group losing in case of conflicts).

Group-level variables defined in vars (like nginx_port under webservers) are available to all hosts in that group. In the Nginx playbook, the template and file tasks reference {{ nginx_port }} and {{ document_root }} — which come from the webservers group vars in this inventory, allowing you to configure different values for a staging webserver group without touching the playbook.

ansible_host for IP mapping

By default, Ansible uses the hostname or alias defined in the inventory (like web1) to connect to the host via SSH. If web1 is not resolvable via DNS, Ansible will fail to connect. ansible_host overrides the connection target, letting you use descriptive logical names in your inventory while still connecting to the correct IP addresses. This is common in private networks where internal IPs are not registered in DNS, or where you want the inventory host name to reflect the server's role rather than its hostname.

Host-level variable overrides

The db1 host demonstrates host-level variable overrides: ansible_user: admin overrides the global ansible_user: ubuntu set in all.vars, because host-level variables always take priority over group and global variables. This is how you handle heterogeneous infrastructure — most hosts share the same SSH user and key, but a few special hosts (like database servers managed by a different team) need different credentials without breaking the defaults for everyone else.

The ansible_port: 22 on db1 is explicit here for clarity. The default SSH port is already 22, so this line has no effect — but it serves as documentation. If your database server exposed SSH on a non-standard port like 2222, setting ansible_port: 2222 at the host level would override the default for that host only.

ansible_python_interpreter for Python 3

Ansible modules run on the remote host using Python. On older Ubuntu systems, python might not be in PATH (only python3 is available), which would cause Ansible to fail with a cryptic error. Setting ansible_python_interpreter: /usr/bin/python3 globally ensures that Ansible always uses Python 3 on all hosts, regardless of whether a python symlink exists. On modern distributions this is the expected default, but making it explicit avoids surprises when targeting a mix of old and new servers.

Tips & variations

Use group_vars directories for cleaner organization

Instead of defining group variables inline in the inventory file, you can create a group_vars/ directory alongside your inventory. Ansible automatically loads group_vars/webservers.yml (or group_vars/webservers/main.yml) for hosts in the webservers group. This keeps the inventory focused on host-to-group mapping while separating configuration into dedicated variable files — easier to review and manage in version control.

Encrypt sensitive host variables with Ansible Vault

Connection variables like ansible_password or database passwords should not be committed to version control in plaintext. Use ansible-vault encrypt_string to encrypt individual values, or ansible-vault encrypt group_vars/dbservers/secrets.yml to encrypt an entire variable file. Run the playbook with ansible-playbook --ask-vault-pass or point to a vault password file with --vault-password-file.

Use a dynamic inventory for cloud environments

For AWS, GCP, Azure, and other cloud providers, Ansible supports dynamic inventories that query the cloud API at runtime to discover hosts and their metadata. For example, amazon.aws.aws_ec2 generates an inventory from your EC2 instances, automatically grouping them by region, VPC, tags, and instance type. This eliminates the need to manually maintain static IP addresses as your infrastructure scales.

Verify the inventory graph

verify inventory
# Show visual group hierarchy
ansible-inventory -i inventory.yml --graph

# Show all hosts and variables as JSON
ansible-inventory -i inventory.yml --list

# Test connectivity to all hosts
ansible all -i inventory.yml -m ping