Ansible Inventory — Hosts And Group Of Hosts
Every Ansible command or playbook needs to know exactly which machines to act on — that’s the entire job of inventory. This is a genuinely vast topic in Ansible: patterns, variable precedence rules, dynamic sources pulling hosts from a cloud provider in real time. We’re keeping things deliberately minimal here — just enough to define hosts and group them sensibly — and coming back for the deeper mechanics, like patterns, in a later chapter.
What Is Inventory?
Inventory is just a file that lists the hosts Ansible should manage. Hosts are usually grouped for easier targeting later on. The file format can be INI or YAML — we’ll be strictly (though not mandatorily) using YAML throughout this series, so get comfortable with it if you aren’t already. Ansible figures out which nodes to act against by looking up this inventory file.
A Very Basic Inventory File: Hosts Only
Here, two hosts are defined: ubuntu and fedora.
hosts:
ubuntu:
ansible_host: 10.0.0.1
ansible_user: alice
fedora:
ansible_host: 10.0.0.2
ansible_user: alice- The
hostskey tells Ansible which hosts to manage. ubuntuis one host, identified byansible_host— this can be a domain name too, not just an IP.ansible_useris the remote user (alice) Ansible will log in as over SSH.fedorais the other host, defined the same way.
Note
Keys like hosts, ansible_user, and ansible_host are provided directly by Ansible — write them exactly as shown. You can find the full list of these built-in connection variables in the Ansible documentation.
Managing Inventory With Groups Of Hosts
We can group ubuntu and fedora under any group name we choose. For the inventory above, both hosts fit naturally under a group called servers:
servers:
hosts:
ubuntu:
ansible_host: 10.0.0.1
ansible_user: alice
fedora:
ansible_host: 10.0.0.2
ansible_user: aliceserversis the group name — everything nested under itshostskey belongs to that group.- The structure of each individual host entry (
ansible_host,ansible_user) hasn’t changed at all — grouping only adds a wrapping layer around hosts you’d otherwise define flatly.
Naming Groups: How And Why
Group names should be meaningful, not generic — a name like servers works for a two-host lab, but tells you nothing once you have fifty hosts across three environments. Group hosts according to whatever dimension you’ll actually want to target later:
- Type of host —
webservers,db_servers,frontend. - Location —
delhi,ap_east_1,downstairs. - Stage —
dev,test,staging,prod.
The reason this matters: groups exist so you can later point a command or playbook at exactly the subset of machines you mean — “every web server,” “everything in staging” — without having to list individual hosts by hand each time. A vague group name defeats that purpose the moment your inventory grows past a handful of hosts.
Two concrete naming rules worth following from the start:
- Prefer underscores over hyphens —
db_servers, notdb-servers. Group names sometimes get used as Jinja2 variable names internally (you’ll see Jinja2 later in this course), and hyphens aren’t valid there. A hyphenated group name works fine until the exact moment it doesn’t, so it’s easier to just never use one. - Don’t reuse Ansible’s own reserved group names — covered next.
The all And ungrouped Groups
Every inventory automatically has two groups you never have to define yourself:
allcontains every host in the inventory, regardless of what other groups they belong to.ungroupedcontains any host that isn’t a member of any group you’ve explicitly defined.
Both names are reserved — don’t create your own group called all or ungrouped, since Ansible already owns those names for this built-in purpose.
When The Same Host Appears In Multiple Groups
A host can belong to more than one group at once, which is normal and often exactly what you want — ubuntu might reasonably belong to both a servers group and a staging group, for instance. Where this gets genuinely risky is when the same connection variable is defined differently for the same host in two different places:
servers:
hosts:
ubuntu:
ansible_host: 10.0.0.1
ansible_user: alice
staging:
hosts:
ubuntu:
ansible_host: 10.0.0.1
ansible_user: bobubuntu is now defined twice, with two different values for ansible_user. Ansible merges these into a single host record internally — and which value actually wins depends on parsing order, which isn’t something you should rely on or try to reason your way around. Ansible’s own documentation is upfront about this: the result of conflicting definitions like this either leads Ansible to add new info or overriding with the latest one — and the latest always doesn’t mean the last one in your definition which may lead to confusion.
The practical takeaway: define a host’s connection variables in exactly one place, and use groups purely for grouping and targeting, not as a second place to redeclare ansible_host or ansible_user with different values. If you genuinely need per-group variable overrides, there’s a correct, deliberate way to do it — but it’s part of the variable precedence rules we’re deferring to a later chapter, not something to improvise here.
Other Gotchas To Watch For
- The host’s key name is just a label, not a real hostname.
ubuntuin these examples doesn’t need to resolve to anything — it’s simply the name Ansible uses to refer to that host in output and commands.ansible_hostis what actually gets connected to. You could name the hostmy-favorite-serverand it would work identically, as long asansible_hostpoints somewhere real. - YAML is indentation-sensitive, and mistakes aren’t always loud. A single misplaced space can silently change which keys belong to which host or group, without necessarily throwing an obvious error. Use an editor with YAML support, and double-check indentation any time something in your inventory behaves unexpectedly.
A Note On Dynamic Inventory
Everything in this chapter has been static inventory — a file you write and maintain by hand. That’s the right way to learn, but it’s worth knowing upfront that few real-world teams actually hand-maintain inventory files once they’re running anything in the cloud. Instances get created and destroyed constantly by autoscaling, and a static file goes stale the moment a host disappears or a new one spins up.
Ansible’s answer to this is dynamic inventory — instead of a hand-written file, a plugin queries a live source (AWS, Azure, GCP, or any other system that knows what your current hosts actually are) and builds the host list automatically, every time Ansible runs. We’ll cover this properly later in the course; for now, just know that the static files you’re writing in this chapter are the foundation, not the endpoint.
Best Practices
- Name groups for how you’ll actually use them — by type, location, or stage — not generically.
- Use underscores, not hyphens, in group and host names.
- Never redefine a host’s connection variables in more than one place. Pick one, and use groups only for grouping.
- Keep your inventory file under version control, the same way you would any other piece of infrastructure configuration — it’s just as important as the playbooks that use it.