Working with Inventory
Learn to organize and manage multiple servers using Ansible inventory files, groups, and host-specific configurations.
As your infrastructure grows, manually listing servers in ad-hoc commands becomes impractical. Ansible's inventory system provides a structured way to organize hosts, define groups, and manage different environments. You'll learn to create flexible inventory configurations that scale from a few servers to hundreds.
Understanding Inventory Structure
Inventory files define which servers Ansible manages and how to connect to them. You can organize hosts into logical groups, set connection parameters, and define variables that apply to specific hosts or groups.
Basic Inventory Formats
Ansible supports both INI and YAML inventory formats. Let's start with INI format since it's more commonly used:
# Basic inventory.ini
[webservers]
web1.example.com
web2.example.com
web3.example.com
[databases]
db1.example.com
db2.example.com
[monitoring]
monitor.example.com
This creates three groups: webservers
, databases
, and monitoring
. Each group contains one or more hosts.
Adding Connection Details
Real servers need connection information. Add these details to your inventory:
[webservers]
web1 ansible_host=192.168.1.10 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/web_key
web2 ansible_host=192.168.1.11 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/web_key
web3 ansible_host=192.168.1.12 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/web_key
[databases]
db-primary ansible_host=192.168.1.20 ansible_user=postgres ansible_port=2222
db-replica ansible_host=192.168.1.21 ansible_user=postgres ansible_port=2222
[loadbalancers]
lb1 ansible_host=10.0.1.5 ansible_user=admin
Here's what these parameters do:
ansible_host
: The actual IP address or hostname to connect toansible_user
: SSH username for connectionsansible_ssh_private_key_file
: Path to SSH private keyansible_port
: SSH port (default is 22)
Using aliases (like web1
, db-primary
) makes your playbooks more readable than using IP addresses directly.
Group Variables and Host Variables
Rather than repeating connection details, use variables to set common parameters for groups or individual hosts.
Group Variables
Create a group_vars
directory and add YAML files named after your groups:
mkdir -p group_vars
Create group_vars/webservers.yml
:
---
ansible_user: ubuntu
ansible_ssh_private_key_file: ~/.ssh/web_servers_key
web_port: 80
nginx_worker_processes: 2
ssl_enabled: false
Create group_vars/databases.yml
:
---
ansible_user: postgres
ansible_port: 2222
db_port: 5432
max_connections: 100
shared_buffers: 256MB
Now your inventory becomes much cleaner:
[webservers]
web1 ansible_host=192.168.1.10
web2 ansible_host=192.168.1.11
web3 ansible_host=192.168.1.12
[databases]
db-primary ansible_host=192.168.1.20
db-replica ansible_host=192.168.1.21
Host Variables
For host-specific settings, create a host_vars
directory:
mkdir -p host_vars
Create host_vars/db-primary.yml
:
---
db_role: primary
backup_enabled: true
replication_user: replicator
Create host_vars/db-replica.yml
:
---
db_role: replica
backup_enabled: false
primary_host: db-primary
These variables are automatically available in playbooks when targeting the specific hosts.
Creating Group Hierarchies
Groups can contain other groups, creating hierarchies that make management easier.
Parent and Child Groups
[webservers]
web1 ansible_host=192.168.1.10
web2 ansible_host=192.168.1.11
[api_servers]
api1 ansible_host=192.168.1.15
api2 ansible_host=192.168.1.16
[databases]
db1 ansible_host=192.168.1.20
db2 ansible_host=192.168.1.21
# Create parent groups
[frontend:children]
webservers
[backend:children]
api_servers
databases
[production:children]
frontend
backend
Now you can target different levels:
ansible all -m ping
: All hostsansible frontend -m shell -a "uptime"
: Just web serversansible backend -m shell -a "df -h"
: API servers and databasesansible production -m setup
: Everything in production
Environment Separation
Organize different environments using group hierarchies:
[web-prod]
web-prod-1 ansible_host=10.0.1.10
web-prod-2 ansible_host=10.0.1.11
[web-staging]
web-staging-1 ansible_host=10.0.2.10
[web-dev]
web-dev-1 ansible_host=192.168.1.10
[db-prod]
db-prod-primary ansible_host=10.0.1.20
db-prod-replica ansible_host=10.0.1.21
[db-staging]
db-staging-1 ansible_host=10.0.2.20
# Environment groupings
[production:children]
web-prod
db-prod
[staging:children]
web-staging
db-staging
[development:children]
web-dev
Create corresponding variable files for each environment in group_vars/
:
group_vars/production.yml
:
---
environment: production
log_level: warn
debug_mode: false
ssl_required: true
backup_retention_days: 30
group_vars/staging.yml
:
---
environment: staging
log_level: info
debug_mode: true
ssl_required: false
backup_retention_days: 7
Dynamic Ranges and Patterns
For large numbers of similar hosts, use patterns to avoid repetitive definitions:
[webservers]
web[01:05].prod.example.com
[databases]
db[a:c].prod.example.com ansible_user=postgres
[workers]
worker-[001:100].example.com
This creates:
web01.prod.example.com
throughweb05.prod.example.com
dba.prod.example.com
,dbb.prod.example.com
,dbc.prod.example.com
worker-001.example.com
throughworker-100.example.com
Advanced Inventory Techniques
Multiple Inventory Files
Split large inventories across multiple files for better organization:
inventories/
├── production/
│ ├── hosts.ini
│ └── group_vars/
│ ├── webservers.yml
│ └── databases.yml
└── staging/
├── hosts.ini
└── group_vars/
├── webservers.yml
└── databases.yml
Specify the inventory directory when running playbooks:
ansible-playbook -i inventories/production/ web-setup.yml
ansible-playbook -i inventories/staging/ web-setup.yml
Using YAML Inventory Format
For complex inventories, YAML format offers more flexibility:
# inventory.yml
all:
children:
webservers:
hosts:
web1:
ansible_host: 192.168.1.10
nginx_worker_processes: 4
web2:
ansible_host: 192.168.1.11
nginx_worker_processes: 2
vars:
ansible_user: ubuntu
web_port: 80
databases:
hosts:
db-primary:
ansible_host: 192.168.1.20
db_role: primary
db-replica:
ansible_host: 192.168.1.21
db_role: replica
vars:
ansible_user: postgres
db_port: 5432
production:
children:
webservers:
databases:
vars:
environment: prod
ssl_enabled: true
Local Connections
For tasks that run on your control machine, add it to inventory:
[control]
localhost ansible_connection=local
[webservers]
web1 ansible_host=192.168.1.10
web2 ansible_host=192.168.1.11
This lets you run tasks locally (like generating configuration files) as part of your playbooks:
- name: Generate deployment report
hosts: control
tasks:
- name: Create deployment summary
copy:
content: |
Deployment completed at {{ ansible_date_time.iso8601 }}
Hosts updated: {{ groups['webservers'] | length }}
dest: ./deployment-report.txt
Testing Your Inventory
Before running playbooks, verify your inventory configuration:
# List all hosts
ansible-inventory --list
# List hosts in specific group
ansible-inventory --list --limit webservers
# Show host variables
ansible-inventory --host web1
# Graph group relationships
ansible-inventory --graph
Test connectivity to different groups:
# Test all hosts
ansible all -m ping
# Test specific environment
ansible production -m ping
# Test specific group
ansible webservers -m ping
Practical Example: Multi-Environment Setup
Here's a complete example showing how to structure inventory for multiple environments:
# inventories/production/hosts.ini
[web-servers]
web-prod-1 ansible_host=10.0.1.10
web-prod-2 ansible_host=10.0.1.11
web-prod-3 ansible_host=10.0.1.12
[api-servers]
api-prod-1 ansible_host=10.0.1.15
api-prod-2 ansible_host=10.0.1.16
[db-servers]
db-prod-primary ansible_host=10.0.1.20
db-prod-replica1 ansible_host=10.0.1.21
db-prod-replica2 ansible_host=10.0.1.22
[load-balancers]
lb-prod-1 ansible_host=10.0.1.5
lb-prod-2 ansible_host=10.0.1.6
[frontend:children]
web-servers
load-balancers
[backend:children]
api-servers
db-servers
[production:children]
frontend
backend
Corresponding playbook that uses this structure:
---
- name: Deploy frontend components
hosts: frontend
become: yes
tasks:
- name: Update frontend applications
git:
repo: '{{ app_repository }}'
dest: '{{ app_directory }}'
version: '{{ app_version }}'
when: inventory_hostname in groups['web-servers']
- name: Configure load balancers
hosts: load-balancers
become: yes
tasks:
- name: Update upstream servers
template:
src: upstream.conf.j2
dest: /etc/nginx/conf.d/upstream.conf
notify: reload nginx
- name: Database maintenance
hosts: db-servers
become: yes
tasks:
- name: Run database backups
shell: pg_dump {{ database_name }} | gzip > /backups/{{ inventory_hostname }}-{{ ansible_date_time.date }}.sql.gz
when: backup_enabled | default(false)
Next Steps
You now understand how to organize hosts using Ansible inventory files, create group hierarchies, and manage variables across different environments. This foundation lets you scale your automation from single servers to complex multi-environment infrastructures.
In the next section, we'll dive deeper into variables and facts - how to make your playbooks flexible and responsive to different system configurations and environments.
The inventory patterns you've learned here will support increasingly sophisticated automation as your infrastructure grows and your playbooks become more complex.
Happy organizing!
Found an issue?