Ansible Error Guide: 'provided hosts list is empty' — Fix Empty Inventory Targeting
Fix Ansible's 'provided hosts list is empty, only localhost is available' warning by repairing inventory paths, group patterns, and dynamic inventory output.
- #ansible
- #automation
- #troubleshooting
- #errors
Stuck on this Ansible error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
Overview
Ansible prints this warning when it starts a run but the inventory it loaded contains no usable hosts, so the play either targets nothing or falls back to an implicit localhost:
[WARNING]: provided hosts list is empty, only localhost is available. Note that
the implicit localhost does not match 'all'
The run usually exits without doing anything, and PLAY RECAP is empty. Nothing failed — that is exactly why it is confusing. The play was never applied to a single real host, but the exit code can still look benign, so scheduled runs silently do nothing for days.
Symptoms
- The warning appears at the top of the run and
PLAY RECAPshows no hosts. ansible-inventory --graphshows only@all:and@ungrouped:with no children.- A play with
hosts: allskips because the implicit localhost does not matchall. - A dynamic inventory script or plugin runs but produces an empty host set.
- The problem appears only in CI, where the inventory path differs from your workstation.
Common Root Causes
- Wrong inventory path — no
-iflag and noinventoryset inansible.cfg, so Ansible falls back to the built-in localhost-only inventory. - Inventory file exists but is empty or malformed — an INI/YAML inventory that parses to zero hosts.
- Group referenced that has no members —
hosts: webserverswhere[webservers]is empty or misspelled. - Dynamic inventory returns nothing — an inventory plugin or script whose backend query returned no hosts (auth failure, wrong filter) but did not error.
--limitfilters everything out — a limit pattern that matches no host in the loaded inventory.- Relative path resolved from the wrong directory —
-i inventory/hostsvalid from the repo root but not from where CI runs.
Diagnostic Workflow
First confirm which inventory Ansible is actually loading and what it contains:
ansible-inventory -i inventory/hosts --graph
ansible-inventory -i inventory/hosts --list
If --graph shows only @all and @ungrouped with no hosts, the inventory is empty as loaded. Check the config resolution and the effective inventory source:
ansible-config dump --only-changed | grep -i inventory
ansible-inventory --graph # with no -i, to see the fallback localhost-only inventory
Test the exact host pattern your play uses, since a group can be empty even when others are populated:
ansible webservers -i inventory/hosts --list-hosts
ansible all -i inventory/hosts --list-hosts
For a dynamic inventory, run the plugin directly and watch for a silent empty result:
ansible-inventory -i aws_ec2.yml --list -vvv
A minimal reproduction — an empty group targeted by a play:
# play.yml
- name: Configure web tier
hosts: webservers # [webservers] group is empty or undefined
tasks:
- ansible.builtin.ping:
ansible-playbook -i inventory/hosts play.yml
# [WARNING]: provided hosts list is empty, only localhost is available.
Example Root Cause Analysis
A nightly CI job stopped configuring servers but never failed. PLAY RECAP was empty every night. Running ansible-inventory -i inventory/hosts --graph from the repo root showed all the hosts correctly, so the inventory file was fine. The difference was the working directory: the pipeline invoked ansible-playbook from a subdirectory, and -i inventory/hosts resolved to a path that did not exist there. With no readable inventory, Ansible fell back to the implicit localhost-only inventory, hosts: all matched nothing, and the run exited 0.
The fix was to pass an absolute inventory path (-i "$CI_PROJECT_DIR/inventory/hosts") and to set inventory explicitly in ansible.cfg so path resolution no longer depended on the caller’s directory. Adding ansible-inventory --graph as a pre-flight step in the pipeline meant an empty inventory would now fail the job loudly instead of silently doing nothing.
Prevention Best Practices
- Set
inventoryexplicitly inansible.cfgso runs do not depend on a-iflag or the current directory. - Add
ansible-inventory --graph(or--list) as a pre-flight check in CI and fail the job if the expected groups are empty. - Use absolute inventory paths in automation, never paths relative to an assumed working directory.
- For dynamic inventory, make the plugin/script raise on backend failure instead of returning an empty list, so an outage is not mistaken for “no hosts.”
- Verify
--limitpatterns with--list-hostsbefore a real run so an over-narrow limit does not silently target nothing.
Quick Command Reference
# Show the loaded inventory as a tree
ansible-inventory -i inventory/hosts --graph
# Dump full host/var data
ansible-inventory -i inventory/hosts --list
# List hosts a pattern would match
ansible <pattern> -i inventory/hosts --list-hosts
# See which inventory config is in effect
ansible-config dump --only-changed | grep -i inventory
# Debug a dynamic inventory plugin
ansible-inventory -i aws_ec2.yml --list -vvv
Conclusion
“provided hosts list is empty” almost never means your hosts are gone — it means Ansible loaded the wrong or an empty inventory and quietly fell back to localhost. Resolve the inventory path deterministically, confirm the loaded host set with ansible-inventory --graph before every real run, and make dynamic inventories fail loudly on error. A run that targets nothing is more dangerous than one that fails, because it hides in a green pipeline.
Fixed it? Get 500 Ansible & DevOps AI prompts — free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.
Stuck on this? Start guided troubleshooting
Open an interactive diagnostic session with this error already loaded. Work a step-by-step plan, record what each check returns, land on a root cause, and export a clean incident summary — no account needed to start.
Did this fix your issue?
Solved it a different way?
Share the fix that worked for you — reviewed, then published to help the next engineer.
That looks like it may contain a secret (key, token, password, or connection string). Please remove it — a note with a detected secret can’t be published.
Thanks — that helps. Published notes appear after a quick review.
More Ansible prompts & error guides
Every Ansible AI prompt and troubleshooting guide, in one place.
Trending errors this week
The error guides other engineers are actually reading right now.
- 1mount: wrong fs type, bad option, bad superblock
- 2Docker 'failed to set up container networking': Fix the Bridge and IP Pool
- 3Docker 'failed to create shim task': How to Fix the containerd Runtime Error
- 4Transport endpoint is not connected
- 5modprobe: FATAL: Module not found
- 6mount: wrong fs type, bad option, bad superblock
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.