ansible/ansible
Fact gathering
When a play sets gather_facts: true (the default), ansible-core runs the setup module on each host and stores the result under ansible_facts. Facts are everything Ansible knows about a host: distribution, kernel version, network interfaces, filesystems, environment, hardware, virtualization vendor.
Components
| Component | File | Purpose |
|---|---|---|
setup module |
lib/ansible/modules/setup.py |
The main entry point — gathers OS, network, hardware, virtualization info |
gather_facts action plugin |
lib/ansible/plugins/action/gather_facts.py |
Dispatches to one or more fact-gathering modules per the user's config |
gather_facts module |
lib/ansible/modules/gather_facts.py |
Module-side companion (shipped with action) |
| Fact collector framework | lib/ansible/module_utils/facts/collector.py |
Pluggable per-domain collectors |
| Default collectors | lib/ansible/module_utils/facts/default_collectors.py |
The set of collectors setup runs |
| Per-domain collectors | lib/ansible/module_utils/facts/{hardware,network,system,virtual}/ |
Subclasses for each subsystem |
| Fact-cache plugins | lib/ansible/plugins/cache/ |
Cache backends (memory, jsonfile) |
package_facts |
lib/ansible/modules/package_facts.py |
Distribution package list (separate, not run by default) |
service_facts |
lib/ansible/modules/service_facts.py |
Service status list (separate, not run by default) |
mount_facts |
lib/ansible/modules/mount_facts.py |
Mount-point facts (separate) |
What setup does
setup is a regular module whose argument_spec accepts:
gather_subset— a list of domain prefixes. Default['all']. You can subset with['network', 'hardware']or exclude with['!hardware'](note the leading!).gather_timeout— per-collector timeout in seconds.filter— glob patterns to limit which fact keys end up in the output.fact_path— directory on the target where custom local fact files are looked up.
The bulk of the work happens in lib/ansible/module_utils/facts/. There's a Collector base class with subclasses per OS family (LinuxNetwork, OpenBSDNetwork, ...), and the setup module runs the default_collectors.collectors list, dispatching each one based on whether it's applicable to the current target.
The collector framework
graph TD
SETUP[setup module] --> AC[ansible_collector.py:<br/>AnsibleFactCollector]
AC --> ITER[Walk default_collectors list]
ITER --> APPLY{Applicable to this OS?}
APPLY -->|yes| COLL[Collector.collect]
APPLY -->|no| SKIP[Skip]
COLL --> MERGE[Merge into facts dict]
MERGE --> NEXT[Next collector]
NEXT --> ITER
ITER -->|done| RESULT[ansible_facts dict]Per-domain code lives under:
lib/ansible/module_utils/facts/system/— distribution, kernel, env vars, locale, services state.lib/ansible/module_utils/facts/hardware/— CPU, memory, devices, mounts.lib/ansible/module_utils/facts/network/— interfaces, IPs, routes.lib/ansible/module_utils/facts/virtual/— virtualization vendor (KVM, VMware, EC2, GCE, Xen, ...).lib/ansible/module_utils/facts/other/— facter, ohai integration if available.lib/ansible/module_utils/facts/namespace.py— flag-based registration.
Each collector is responsible for catching its own exceptions and returning an empty dict on failure rather than blowing up the entire setup run.
What you actually get
ansible_facts ends up looking like:
ansible_facts:
ansible_distribution: Ubuntu
ansible_distribution_version: "24.04"
ansible_kernel: 6.8.0-1014-aws
ansible_python_version: "3.12.3"
ansible_default_ipv4:
address: 10.0.1.42
interface: eth0
ansible_interfaces:
- lo
- eth0
ansible_memtotal_mb: 7763
ansible_processor_count: 2
ansible_virtualization_type: kvm
ansible_virtualization_role: guest
ansible_pkg_mgr: apt
ansible_service_mgr: systemd
...Hundreds of keys; full reference is in lib/ansible/modules/setup.py's RETURN block.
Custom (local) facts
If fact_path is set (default /etc/ansible/facts.d/ on Linux), setup reads files there and exposes their contents under ansible_local. Files can be:
*.fact— INI/JSON files; parsed and exposed as nested dicts.- Executables — run, stdout parsed as JSON.
This lets administrators ship per-host metadata without modifying the playbook.
The fact cache
Repeatedly running setup against the same hosts is expensive. Ansible can cache facts between runs via cache plugins (lib/ansible/plugins/cache/):
memory— process-lifetime cache only. Default. Useful within one run for tasks that re-look-up facts.jsonfile— per-host JSON files inFACT_CACHE_DIR(default~/.ansible/facts_cache/).- Collections add more (
community.generalshipsredis,mongodb,memcached, etc.).
Configure with:
[defaults]
fact_caching = jsonfile
fact_caching_connection = ~/.ansible/facts_cache/
fact_caching_timeout = 86400When the cache is enabled and fresh, plays can set gather_facts: false and still see the cached fact data — the variable manager pulls from the cache transparently.
How facts flow into the variable scope
Facts are tagged differently from user-authored vars:
setup's output JSON is unpacked bylib/ansible/plugins/action/gather_facts.pyand merged intoVariableManager's per-host fact namespace.- Facts are not trusted as templates. A fact value containing
{{ }}is left as-is — see Templating on the trust model. - Facts are accessible as
ansible_facts.<key>and as bare top-level vars (ansible_distribution) for backwards compatibility. The "INJECT_FACTS_AS_VARS" config option controls the latter.
gather_facts action plugin dispatch
The gather_facts action plugin (lib/ansible/plugins/action/gather_facts.py) is what the play actually invokes. It:
- Reads the per-play
gather_factssetting and the globalDEFAULT_GATHERINGconfig. - Dispatches to one or more configured modules. Default is
setup. Network device collections use a different one (cisco.ios.ios_facts, etc.). - Merges results into the variable scope.
The FACT_GATHERING_MODULES config option controls which modules run; collections can register new fact-gathering modules so a "smart gather" picks the right one per platform.
package_facts, service_facts, mount_facts
These three modules are the "expensive" fact gatherers — they're slow and not run by default. You invoke them as regular tasks when you need their data:
- name: gather installed packages
ansible.builtin.package_facts:
manager: autoEach populates a specific subset of ansible_facts (ansible_facts.packages, ansible_facts.services, etc.).
Integration points
- Imported by:
lib/ansible/plugins/action/gather_facts.py,lib/ansible/plugins/action/setup.py,lib/ansible/vars/manager.py(cache lookup),lib/ansible/inventory/manager.py(whenINJECT_FACTS_AS_VARS). - Imports:
lib/ansible/module_utils/facts/(the entire collector tree),lib/ansible/plugins/cache/(fact caching).
Entry points for modification
- Adding a new fact — extend the relevant collector under
lib/ansible/module_utils/facts/<domain>/. Register it indefault_collectors.py. - A new domain entirely — add a directory under
module_utils/facts/<new>/with aCollectorsubclass; add a key prefix tosetup'sgather_subsetparser. - A new fact-cache backend — write a cache plugin in a collection extending
lib/ansible/plugins/cache/__init__.py:BaseCacheModule.
Cross-links
- Modules and actions — the module/action plugin pairing for
setup/gather_facts. - Templating — why fact strings aren't templated.
- Plugins → Cache — fact-cache backends.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.