Ansible facts: Gather, Use, and Create Custom Facts
By Luca Berton · Published 2024-01-01 · Category: installation
Complete guide to Ansible facts. Learn how to gather system facts, use them in playbooks, create custom facts, cache facts for performance, and use set_fact.
Facts are variables automatically discovered about remote hosts — OS, IP addresses, memory, CPU, disks, and more. They're the foundation of conditional logic and cross-platform playbooks.
Gathering Facts
# Facts are gathered automatically at play start
- hosts: all
# gather_facts: true ← default
tasks:
- ansible.builtin.debug:
msg: "{{ ansible_hostname }} runs {{ ansible_distribution }} {{ ansible_distribution_version }}"
See All Facts
# Dump all facts for a host
ansible host -m setup
# Filter specific facts
ansible host -m setup -a "filter=ansible_distribution*"
ansible host -m setup -a "filter=ansible_default_ipv4"
ansible host -m setup -a "filter=ansible_memory_mb"
See also: Ansible gather_facts & ansible_facts: Access Host Information (Guide)
Most Useful Facts
Operating System
ansible_distribution: "Ubuntu" # Ubuntu, CentOS, Debian, RedHat, Amazon
ansible_distribution_version: "24.04"
ansible_distribution_major_version: "24"
ansible_distribution_release: "noble"
ansible_os_family: "Debian" # Debian, RedHat, Suse, Darwin, Windows
ansible_system: "Linux" # Linux, Darwin, Windows
ansible_architecture: "x86_64"
ansible_kernel: "6.8.0-45-generic"
Network
ansible_hostname: "web01"
ansible_fqdn: "web01.example.com"
ansible_default_ipv4:
address: "10.0.1.10"
interface: "eth0"
gateway: "10.0.1.1"
netmask: "255.255.255.0"
ansible_all_ipv4_addresses: ["10.0.1.10", "172.17.0.1"]
Hardware
ansible_memtotal_mb: 8192
ansible_memfree_mb: 4096
ansible_processor_vcpus: 4
ansible_processor: ["Intel(R) Xeon(R) CPU E5-2686 v4"]
ansible_devices:
sda: { size: "100.00 GB", model: "Virtual disk" }
Timestamps
ansible_date_time:
iso8601: "2026-04-13T01:00:00Z"
date: "2026-04-13"
hour: "01"
tz: "UTC"
Using Facts for Conditional Logic
OS-Specific Tasks
- name: Install packages (Debian)
ansible.builtin.apt:
name: "{{ packages }}"
state: present
update_cache: true
when: ansible_os_family == "Debian"
- name: Install packages (RedHat)
ansible.builtin.dnf:
name: "{{ packages }}"
state: present
when: ansible_os_family == "RedHat"
Memory-Based Configuration
- name: Configure JVM heap based on available memory
ansible.builtin.template:
src: jvm.options.j2
dest: /etc/myapp/jvm.options
vars:
heap_size_mb: "{{ (ansible_memtotal_mb * 0.5) | int }}"
Version-Specific Logic
- name: Use new syntax for Ubuntu 24+
ansible.builtin.command: new-command --v2
when: >
ansible_distribution == "Ubuntu" and
ansible_distribution_major_version | int >= 24
See also: Ansible Fix 'VARIABLE IS NOT DEFINED' Error: Undefined Variables
Custom Facts (Local Facts)
Create /etc/ansible/facts.d/.fact files on remote hosts:
# Deploy a custom fact file
- name: Deploy application facts
ansible.builtin.copy:
content: |
[application]
name=myapp
version=2.5.0
environment=production
deployed_by=ansible
deployed_at={{ ansible_date_time.iso8601 }}
dest: /etc/ansible/facts.d/myapp.fact
mode: '0644'
# Re-gather facts to pick up the new file
- name: Reload facts
ansible.builtin.setup:
filter: ansible_local
# Use the custom fact
- ansible.builtin.debug:
msg: "App version: {{ ansible_local.myapp.application.version }}"
JSON Custom Facts
- name: Deploy JSON fact
ansible.builtin.copy:
content: |
{
"app_version": "2.5.0",
"features": ["api", "worker", "scheduler"],
"config": {
"port": 8080,
"workers": 4
}
}
dest: /etc/ansible/facts.d/myapp.fact
mode: '0644'
# Access: ansible_local.myapp.app_version
# Access: ansible_local.myapp.config.port
Executable Fact Scripts
#!/bin/bash
# /etc/ansible/facts.d/disk_usage.fact
# Must output JSON
echo "{"
echo " \"root_usage_percent\": $(df / --output=pcent | tail -1 | tr -d ' %'),"
echo " \"data_usage_percent\": $(df /data --output=pcent 2>/dev/null | tail -1 | tr -d ' %' || echo 0)"
echo "}"
# Access: ansible_local.disk_usage.root_usage_percent
Fact Caching
Speed up playbooks by caching facts:
# ansible.cfg
[defaults]
gathering = smart # Only gather if facts not cached
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts_cache
fact_caching_timeout = 3600 # Cache for 1 hour
# Or use Redis
# fact_caching = redis
# fact_caching_connection = localhost:6379
See also: ansible.builtin.set_fact Module: Set Variables Dynamically (Complete Guide)
Disable Fact Gathering
# Skip fact gathering for faster execution
- hosts: all
gather_facts: false
tasks:
- name: Simple task that doesn't need facts
ansible.builtin.ping:
# Gather facts later if needed
- name: Gather facts now
ansible.builtin.setup:
when: need_facts | default(false)
set_fact vs Gathered Facts
# Gathered facts: automatic system info
# set_fact: custom runtime variables
- name: Get app version
ansible.builtin.command: /opt/app/bin/version
register: version_output
changed_when: false
- name: Create clean fact from output
ansible.builtin.set_fact:
app_version: "{{ version_output.stdout | trim }}"
app_needs_upgrade: "{{ version_output.stdout is version('2.0', '<') }}"
Accessing Facts from Other Hosts
# Use hostvars to get facts from any host in the play
- name: Configure app to connect to database
ansible.builtin.template:
src: db-config.j2
dest: /etc/myapp/database.yml
vars:
db_host: "{{ hostvars[groups['databases'][0]].ansible_default_ipv4.address }}"
db_memory: "{{ hostvars[groups['databases'][0]].ansible_memtotal_mb }}"
FAQ
How do I see what facts are available?
Run ansible hostname -m setup to see all facts. Use ansible hostname -m setup -a "filter=ansible_memory*" to search for specific facts. Facts follow the pattern ansible_.
Why is fact gathering slow?
Fact gathering runs setup on every host, collecting hardware, network, and OS info. Speed it up with gather_subset to collect only what you need:
- hosts: all
gather_facts: true
gather_subset:
- network
- '!hardware' # Skip slow hardware detection
Can I use facts in inventory?
No — facts are gathered at runtime, not during inventory parsing. For inventory-time variables, use group_vars, host_vars, or dynamic inventory scripts.
What's the difference between ansible_facts and top-level facts?
Both work. ansible_facts['distribution'] and ansible_distribution reference the same data. The ansible_facts dictionary is the canonical source; top-level variables are injected for convenience.
Conclusion
Facts are Ansible's discovery layer — they tell your playbooks what they're working with. Use gathered facts for conditional logic and cross-platform support, custom facts for application metadata, and fact caching for performance. Every serious Ansible deployment relies on facts.
Related Articles
• Ansible set_fact vs vars vs extra_vars • Ansible Conditionals (when) • Ansible Variable Precedence Guide • Ansible Inventory Guide • ansible_hostname vs inventory_hostname vs ansible_fqdn • Ansible gather_facts & ansible_facts GuideCategory: installation