Open-Source Wikis

/

Ansible

/

Features

/

Fact gathering

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 in FACT_CACHE_DIR (default ~/.ansible/facts_cache/).
  • Collections add more (community.general ships redis, mongodb, memcached, etc.).

Configure with:

[defaults]
fact_caching = jsonfile
fact_caching_connection = ~/.ansible/facts_cache/
fact_caching_timeout = 86400

When 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 by lib/ansible/plugins/action/gather_facts.py and merged into VariableManager'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:

  1. Reads the per-play gather_facts setting and the global DEFAULT_GATHERING config.
  2. Dispatches to one or more configured modules. Default is setup. Network device collections use a different one (cisco.ios.ios_facts, etc.).
  3. 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: auto

Each 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 (when INJECT_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 in default_collectors.py.
  • A new domain entirely — add a directory under module_utils/facts/<new>/ with a Collector subclass; add a key prefix to setup's gather_subset parser.
  • A new fact-cache backend — write a cache plugin in a collection extending lib/ansible/plugins/cache/__init__.py:BaseCacheModule.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Fact gathering – Ansible wiki | Factory