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 thesetup 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 inwhen 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, setgather_facts: no in your playbook:
- name: Play without facts
hosts: all
gather_facts: no
2. Gather Specific Facts
Use thegather_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 thesetup 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 usinggather_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 templates • using handlers in Ansible playbooks • Ansible Vault guide • multi-condition Ansible when clauses • building an Ansible inventoryCategory: installation