AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

Ansible Facts: What They Are & How to Use Them (Complete Guide)

By Luca Berton · Published 2024-01-01 · Category: installation

What are Ansible facts? Learn how gather_facts, setup module, and custom facts work. Access OS, network, hardware info with practical playbook examples.

Ansible facts are a cornerstone of Ansible automation, providing valuable information about managed nodes that can be dynamically used in playbooks. This article explains what facts are, how they work, and their role in enhancing automation workflows.

What Are Ansible Facts?

Ansible facts are system variables automatically collected from managed nodes during playbook execution. They include details about the system’s hardware, operating system, network interfaces, and more. These facts enable playbooks to adapt dynamically to different environments.

Key Features of Facts:

Automatically Gathered: By default, Ansible collects facts using the setup module at the start of a playbook run. • Dynamic Variables: Facts provide real-time information about target systems. • Versatile Usage: Use facts in conditions, tasks, templates, and handlers.

See also: Can Ansible Be Used to Manage Windows Systems?

How to Use Ansible Facts

1. Accessing Facts

Facts are accessible as variables in your playbooks. For example:
   - name: Print operating system
     debug:
       msg: "The OS is {{ ansible_os_family }}"
   

2. Common Facts

OS Information:
     {{ ansible_distribution }} - Operating system name
     {{ ansible_distribution_version }} - OS version
     
Network Details:
     {{ ansible_default_ipv4.address }} - Default IPv4 address
     {{ ansible_fqdn }} - Fully qualified domain name
     
Hardware Info:
     {{ ansible_processor }} - CPU details
     {{ ansible_memtotal_mb }} - Total memory in MB
     

3. Using Facts in Conditions

Facts are often used in when statements to create conditional tasks:
   - name: Install Apache on Debian systems
     apt:
       name: apache2
       state: present
     when: ansible_os_family == "Debian"
   

4. Custom Facts

Define your own facts by placing files in /etc/ansible/facts.d/ on managed nodes. For example:
   echo '{"custom_fact": "value"}' > /etc/ansible/facts.d/custom.json
   

Access this custom fact in your playbook:

   - name: Print custom fact
     debug:
       msg: "Custom fact value: {{ ansible_local.custom_fact }}"
   

Controlling Fact Gathering

1. Disabling Fact Gathering

To skip automatic fact gathering, set gather_facts: no in your playbook:
   - name: Play without facts
     hosts: all
     gather_facts: no
   

2. Gather Specific Facts

Use the gather_subset option to limit the scope of facts:
   - name: Gather minimal facts
     setup:
       gather_subset:
         - network
         - hardware
   

3. Gathering Facts On-Demand

Manually gather facts at any point using the setup module:
   - name: Re-gather facts
     setup:
   

See also: Can Ansible Manage Windows? Complete Windows Automation Guide

Best Practices for Using Facts

Filter Facts: Avoid unnecessary overhead by gathering only required facts using gather_subset. • Use Custom Facts: Create custom facts for environment-specific information. • Secure Data: Encrypt sensitive facts using Ansible Vault. • Debug Facts: Use the setup module with filters to explore available facts:
  ansible localhost -m setup -a "filter=ansible_*"
  

Common Use Cases for Ansible Facts

Dynamic Configuration: Adjust configurations based on system properties, such as memory or OS. Inventory Grouping: Use facts to dynamically group hosts in playbooks. Conditional Execution: Ensure tasks are executed only when certain conditions are met.

See also: Ansible on Windows: Complete Guide to Windows Automation (2026)

Conclusion

Ansible facts play a vital role in making automation intelligent and adaptive. By leveraging facts, you can create flexible and efficient playbooks that respond dynamically to the state of managed systems.

Learn More About Ansible Facts

View All Facts

ansible localhost -m setup
ansible webserver1 -m setup | less

Common Facts

- debug:
    msg:
      - "OS: {{ ansible_distribution }} {{ ansible_distribution_version }}"
      - "Kernel: {{ ansible_kernel }}"
      - "CPU: {{ ansible_processor_cores }} cores"
      - "RAM: {{ ansible_memtotal_mb }} MB"
      - "IP: {{ ansible_default_ipv4.address }}"
      - "Hostname: {{ ansible_hostname }}"
      - "FQDN: {{ ansible_fqdn }}"
      - "Python: {{ ansible_python_version }}"
      - "Architecture: {{ ansible_architecture }}"

Filter Facts

# Specific fact
ansible localhost -m setup -a "filter=ansible_distribution*"
ansible localhost -m setup -a "filter=ansible_memtotal_mb"
ansible localhost -m setup -a "filter=ansible_default_ipv4"

Use Facts in Conditionals

- name: Install on Debian
  ansible.builtin.apt:
    name: nginx
  when: ansible_os_family == "Debian"
  become: true

- name: Install on RedHat ansible.builtin.yum: name: nginx when: ansible_os_family == "RedHat" become: true

- name: Only on 64-bit debug: msg="64-bit system" when: ansible_architecture == "x86_64"

Facts in Templates

# config.j2
server_name={{ ansible_fqdn }}
bind_address={{ ansible_default_ipv4.address }}
worker_processes={{ ansible_processor_vcpus }}
max_memory={{ (ansible_memtotal_mb * 0.8) | int }}m

Disable Fact Gathering

# Skip for faster execution
- hosts: all
  gather_facts: false
  tasks:
    - name: Quick task (no facts needed)
      command: uptime

Custom Facts

# On remote host: /etc/ansible/facts.d/custom.fact
# Must be executable or .json/.ini
# /etc/ansible/facts.d/app.fact
[app]
version=2.5.0
environment=production
# Deploy custom fact with Ansible
- name: Create facts directory
  file:
    path: /etc/ansible/facts.d
    state: directory
  become: true

- name: Deploy custom fact copy: content: | [app] version={{ app_version }} deployed={{ ansible_date_time.iso8601 }} dest: /etc/ansible/facts.d/app.fact mode: '0644' become: true

# Access: {{ ansible_local.app.app.version }}

set_fact (Runtime Facts)

- set_fact:
    app_url: "http://{{ ansible_default_ipv4.address }}:8080"
    is_production: "{{ ansible_hostname is match('prod-.*') }}"

Cache Facts

# ansible.cfg
[defaults]
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts_cache
fact_caching_timeout = 86400

Fact Categories

| Category | Examples | |----------|---------| | OS | ansible_distribution, ansible_os_family, ansible_kernel | | Hardware | ansible_processor_cores, ansible_memtotal_mb, ansible_architecture | | Network | ansible_default_ipv4, ansible_interfaces, ansible_dns | | Disk | ansible_mounts, ansible_devices | | Date/Time | ansible_date_time.date, ansible_date_time.epoch | | User | ansible_user_id, ansible_env |

FAQ

Why is gather_facts slow?

It collects extensive system info. Speed up with gather_subset:

gather_facts: true
gather_subset:
  - min   # Minimal facts only
  - network  # Add network facts

How do I access facts from other hosts?

msg: "DB IP: {{ hostvars['db-server'].ansible_default_ipv4.address }}"

ansible_facts vs ansible_* variables?

Both work. ansible_facts['distribution'] is equivalent to ansible_distribution. The ansible_facts dict is the modern form.

View All Facts

ansible web1 -m setup

Common Facts

- debug:
    msg: |
      Hostname: {{ ansible_hostname }}
      FQDN: {{ ansible_fqdn }}
      OS: {{ ansible_distribution }} {{ ansible_distribution_version }}
      Family: {{ ansible_os_family }}
      Kernel: {{ ansible_kernel }}
      Arch: {{ ansible_architecture }}
      IP: {{ ansible_default_ipv4.address }}
      CPU: {{ ansible_processor_vcpus }} cores
      RAM: {{ ansible_memtotal_mb }} MB
      Disk: {{ ansible_mounts[0].size_total | int / 1073741824 | round(1) }} GB
      Python: {{ ansible_python_version }}

Use Facts in Conditionals

# OS-specific tasks
- apt: { name: nginx }
  when: ansible_os_family == "Debian"

- yum: { name: nginx } when: ansible_os_family == "RedHat"

# Version check - debug: msg="Modern Ubuntu" when: - ansible_distribution == "Ubuntu" - ansible_distribution_major_version | int >= 22

# Architecture - get_url: url: "https://releases.example.com/app-{{ ansible_architecture }}.tar.gz" dest: /tmp/

Filter Specific Facts

# Only network facts
ansible web1 -m setup -a "filter=ansible_default_ipv4"

# Wildcard filter ansible web1 -m setup -a "filter=ansible_*_mb"

Custom Facts

# On remote host: /etc/ansible/facts.d/app.fact
# (must be .fact extension, JSON or INI format)
{
    "app_version": "2.5.0",
    "environment": "production",
    "features": ["api", "worker", "scheduler"]
}
# Access custom facts
- debug:
    msg: "App version: {{ ansible_local.app.app_version }}"

Set Facts Dynamically

- set_fact:
    is_production: "{{ ansible_hostname is match('prod-.*') }}"
    total_memory_gb: "{{ ansible_memtotal_mb / 1024 | round(1) }}"

- template: src: config.j2 dest: /etc/myapp/config when: is_production

Disable Fact Gathering

# Skip for faster execution
- hosts: all
  gather_facts: false
  tasks:
    - ping:  # Doesn't need facts

Fact Caching

# ansible.cfg — cache facts to speed up runs
[defaults]
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 86400  # 24 hours

Network Facts

- debug:
    msg: |
      Default interface: {{ ansible_default_ipv4.interface }}
      IP: {{ ansible_default_ipv4.address }}
      Gateway: {{ ansible_default_ipv4.gateway }}
      MAC: {{ ansible_default_ipv4.macaddress }}
      DNS: {{ ansible_dns.nameservers | join(', ') }}
      All IPs: {{ ansible_all_ipv4_addresses | join(', ') }}

Disk Facts

- debug:
    msg: "{{ item.mount }}: {{ (item.size_available / 1073741824) | round(1) }}GB free"
  loop: "{{ ansible_mounts }}"
  when: item.size_available / item.size_total < 0.1  # Less than 10% free

FAQ

gather_facts vs setup module?

Same thing — gather_facts: true (default) runs the setup module automatically at play start.

How to add custom facts without root?

Place .fact files in /etc/ansible/facts.d/ (needs root), or use set_fact in your playbook (no root needed).

Can I gather facts from Windows?

Yes — Ansible gathers Windows facts automatically via WinRM. Access them the same way: ansible_os_name, ansible_win_domain, etc.

What Are Facts?

Facts are system information Ansible automatically collects from remote hosts:

ansible_hostname: web1
ansible_os_family: Debian
ansible_distribution: Ubuntu
ansible_distribution_version: "24.04"
ansible_default_ipv4:
  address: 192.168.1.10
ansible_memtotal_mb: 8192
ansible_processor_vcpus: 4

View All Facts

ansible web1 -m setup
ansible web1 -m setup -a "filter=ansible_distribution*"

Use Facts in Playbooks

- hosts: all
  tasks:
    - debug:
        msg: "{{ ansible_hostname }} runs {{ ansible_distribution }} {{ ansible_distribution_version }}"

- apt: { name: nginx } when: ansible_os_family == "Debian"

- yum: { name: nginx } when: ansible_os_family == "RedHat"

Common Facts

# OS
ansible_os_family          # Debian, RedHat, Suse
ansible_distribution       # Ubuntu, CentOS, Rocky
ansible_distribution_version  # 24.04, 9.3

# Network ansible_default_ipv4.address # Primary IP ansible_all_ipv4_addresses # All IPs ansible_fqdn # FQDN

# Hardware ansible_memtotal_mb # Total RAM in MB ansible_processor_vcpus # CPU count ansible_architecture # x86_64, aarch64

# Storage ansible_mounts # Mounted filesystems ansible_devices # Block devices

Gather Subsets (Faster)

# Only gather what you need
- hosts: all
  gather_facts: true
  module_defaults:
    setup:
      gather_subset:
        - network
        - hardware

Disable Facts

- hosts: all
  gather_facts: false  # Faster if you don't need them

Custom Facts

# On remote host: /etc/ansible/facts.d/myapp.fact
[general]
app_version=2.5.0
app_port=8080
# Access as:
- debug:
    msg: "App version: {{ ansible_local.myapp.general.app_version }}"

Cached Facts

# ansible.cfg
[defaults]
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible-facts
fact_caching_timeout = 3600

FAQ

Facts vs variables?

Facts are auto-collected system info. Variables are user-defined values. Both are accessed with {{ }}.

Why is gather_facts slow?

It collects everything. Use gather_subset to limit, or gather_facts: false + manual setup for specific data.

Custom facts not showing?

Check: file is in /etc/ansible/facts.d/, has .fact extension, is executable (for JSON/script facts) or valid INI.

Related Articles

Jinja2 filters in Ansible templatesusing handlers in Ansible playbooksAnsible Vault guidemulti-condition Ansible when clausesbuilding an Ansible inventory

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home