Ansible gather_facts & ansible_facts: Access Host Information (Guide)
By Luca Berton · Published 2024-01-01 · Category: installation
Complete guide to Ansible gather_facts and ansible_facts. Access OS type, IP addresses, memory, disk space, and hardware info.
Ansible facts are system properties collected from managed hosts — OS type, IP addresses, memory, CPU, disk space, and more. They're gathered automatically at the start of each play and available as variables throughout your playbook.
How Facts Work
- name: Show facts
hosts: all
# gather_facts: true (default — happens automatically)
tasks:
- ansible.builtin.debug:
msg: |
OS: {{ ansible_distribution }} {{ ansible_distribution_version }}
IP: {{ ansible_default_ipv4.address }}
Memory: {{ ansible_memtotal_mb }} MB
CPUs: {{ ansible_processor_vcpus }}
See also: Ansible Facts: Gather, Use, and Create Custom Facts (Complete Guide)
Most Used Facts
# Operating system
ansible_distribution # "Ubuntu", "CentOS", "RedHat"
ansible_distribution_version # "24.04", "9.3"
ansible_distribution_release # "noble", "Plow"
ansible_os_family # "Debian", "RedHat", "Suse"
ansible_kernel # "6.8.0-41-generic"
ansible_architecture # "x86_64", "aarch64"
# Network
ansible_default_ipv4.address # Primary IPv4
ansible_default_ipv4.interface # "eth0", "ens3"
ansible_all_ipv4_addresses # All IPv4 addresses (list)
ansible_hostname # Short hostname
ansible_fqdn # Fully qualified domain name
# Hardware
ansible_memtotal_mb # Total RAM in MB
ansible_memfree_mb # Free RAM in MB
ansible_processor_vcpus # Number of CPUs
ansible_devices # Disk devices
# Python
ansible_python_version # "3.11.5"
ansible_python.executable # "/usr/bin/python3"
Conditional Tasks with Facts
# OS-specific tasks
- name: Install on Debian
ansible.builtin.apt:
name: nginx
state: present
when: ansible_os_family == "Debian"
- name: Install on RedHat
ansible.builtin.dnf:
name: nginx
state: present
when: ansible_os_family == "RedHat"
# Version checks
- name: Only on Ubuntu 22.04+
ansible.builtin.debug:
msg: "Running on modern Ubuntu"
when:
- ansible_distribution == "Ubuntu"
- ansible_distribution_version is version("22.04", ">=")
# Memory-based decisions
- name: Configure swap if low memory
ansible.builtin.include_tasks: setup-swap.yml
when: ansible_memtotal_mb < 2048
See also: Generate Clean YAML Output from Ansible Facts
Disable Fact Gathering
# Skip facts for faster execution
- name: Simple task (no facts needed)
hosts: all
gather_facts: false
tasks:
- name: Copy file
ansible.builtin.copy:
src: config.yml
dest: /etc/app/config.yml
When to disable: Tasks that don't use any ansible_ variables. Saves 1-5 seconds per host.
Gather Specific Facts Only
# Only gather network facts
- name: Network info only
hosts: all
gather_facts: false
tasks:
- name: Gather network subset
ansible.builtin.setup:
gather_subset:
- network
- ansible.builtin.debug:
msg: "IP: {{ ansible_default_ipv4.address }}"
Available subsets: all, min, hardware, network, virtual, ohai, facter
# Exclude slow subsets
- ansible.builtin.setup:
gather_subset:
- "!hardware" # Skip hardware detection (slow on some systems)
See also: Ansible facts: Gather, Use, and Create Custom Facts
Filter Facts
# Only get facts matching a pattern
- ansible.builtin.setup:
filter: "ansible_mem*"
register: mem_facts
# Result: only ansible_memtotal_mb, ansible_memfree_mb, etc.
Custom Facts (Local Facts)
Place .fact files on managed hosts at /etc/ansible/facts.d/:
# /etc/ansible/facts.d/app.fact
[general]
app_version=2.5.1
environment=production
maintainer=ops-team
# Deploy custom fact
- name: Create facts directory
ansible.builtin.file:
path: /etc/ansible/facts.d
state: directory
mode: '0755'
- name: Deploy application fact
ansible.builtin.copy:
content: |
[app]
version={{ app_version }}
deploy_date={{ ansible_date_time.date }}
dest: /etc/ansible/facts.d/myapp.fact
mode: '0644'
# Access custom facts
- name: Re-gather facts
ansible.builtin.setup:
- name: Show custom fact
ansible.builtin.debug:
msg: "App version: {{ ansible_local.myapp.app.version }}"
Fact Caching
# ansible.cfg
[defaults]
gathering = smart # Only gather if not cached
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts_cache
fact_caching_timeout = 3600 # Cache for 1 hour
Other backends: redis, memcached, yaml, jsonfile
set_fact: Create Runtime Facts
- name: Calculate derived fact
ansible.builtin.set_fact:
swap_size_mb: "{{ ansible_memtotal_mb * 2 }}"
is_production: "{{ 'prod' in inventory_hostname }}"
- name: Use derived fact
ansible.builtin.debug:
msg: "Swap: {{ swap_size_mb }}MB, Production: {{ is_production }}"
View All Facts
# From command line
ansible hostname -m setup
# Filter from command line
ansible hostname -m setup -a "filter=ansible_distribution*"
# In a playbook — dump all facts
- name: Save facts to file
ansible.builtin.copy:
content: "{{ ansible_facts | to_nice_json }}"
dest: "/tmp/facts_{{ inventory_hostname }}.json"
delegate_to: localhost
Practical Examples
Inventory Report
- name: Generate server report
hosts: all
tasks:
- name: Write report line
ansible.builtin.lineinfile:
path: /tmp/server-report.csv
line: "{{ inventory_hostname }},{{ ansible_distribution }},{{ ansible_distribution_version }},{{ ansible_default_ipv4.address }},{{ ansible_memtotal_mb }}MB,{{ ansible_processor_vcpus }}CPU"
create: true
delegate_to: localhost
OS-Agnostic Package Variable
vars:
package_names:
Debian:
web: apache2
db: postgresql
RedHat:
web: httpd
db: postgresql-server
tasks:
- name: Install web server
ansible.builtin.package:
name: "{{ package_names[ansible_os_family].web }}"
state: present
FAQ
What's the difference between ansible_facts and ansible_ variables?
They're the same data. ansible_facts['distribution'] and ansible_distribution both work. The ansible_facts dictionary is the modern format; the flat ansible_* variables are injected for backward compatibility (controlled by INJECT_FACTS_AS_VARS in ansible.cfg).
How do I speed up fact gathering?
Setgather_facts: false when you don't need facts
Use gather_subset to collect only what you need
Enable fact caching (gathering = smart)
Exclude slow subsets: gather_subset: ['!hardware']
How do I get facts from another host?
Use hostvars:
- ansible.builtin.debug:
msg: "DB server IP: {{ hostvars['dbserver'].ansible_default_ipv4.address }}"
Why are facts empty or missing?
The remote host may lack Python, or gather_facts: false is set. Check that Python is installed and the setup module can run: ansible hostname -m setup.
Conclusion
Facts are the foundation of dynamic, OS-aware playbooks. Use them for conditional tasks, template variables, and inventory reporting. Disable gathering when not needed for speed, cache facts for large inventories, and create custom facts for application-specific metadata.
Related Articles
• Ansible inventory_hostname vs ansible_hostname • Ansible set_fact vs vars vs extra_vars • Ansible Performance TuningCategory: installation