Introduction
Ansible facts are system properties collected automatically from managed hosts — OS name, IP addresses, CPU count, memory, disk space, network interfaces, and more. They're available as variables in your playbooks, letting you write conditional logic, generate templates, and make decisions based on actual system state.
How Facts Work
By default, Ansible runs the setup module at the start of every play to collect facts:
---
- name: Show facts
hosts: all
# gather_facts: true (default)
tasks:
- name: Print OS
ansible.builtin.debug:
msg: "{{ ansible_distribution }} {{ ansible_distribution_version }}"
# Output: "Ubuntu 24.04" or "RedHat 9.3"
Common Facts Reference
| Fact | Example Value | Description |
|---|---|---|
ansible_hostname | web-01 | Short hostname |
ansible_fqdn | web-01.example.com | Fully qualified domain name |
ansible_distribution | Ubuntu | OS distribution |
ansible_distribution_version | 24.04 | OS version |
ansible_distribution_major_version | 24 | Major version |
ansible_os_family | Debian | OS family (Debian, RedHat, etc.) |
ansible_kernel | 6.5.0-44-generic | Kernel version |
ansible_architecture | x86_64 | CPU architecture |
ansible_default_ipv4.address | 10.0.0.5 | Primary IPv4 address |
ansible_default_ipv4.interface | eth0 | Primary network interface |
ansible_all_ipv4_addresses | ["10.0.0.5", "172.17.0.1"] | All IPv4 addresses |
ansible_processor_vcpus | 4 | CPU core count |
ansible_memtotal_mb | 8192 | Total RAM in MB |
ansible_memfree_mb | 4096 | Free RAM in MB |
ansible_swaptotal_mb | 2048 | Total swap in MB |
ansible_devices | {sda: {...}} | Disk devices |
ansible_mounts | [{mount: "/", ...}] | Mounted filesystems |
ansible_env | {HOME: "/root", ...} | Environment variables |
ansible_user_id | root | Current user |
ansible_python_version | 3.12.1 | Python version |
ansible_virtualization_type | kvm | Virtualization type |
ansible_service_mgr | systemd | Service manager |
ansible_pkg_mgr | apt | Package manager |
ansible_date_time.iso8601 | 2026-04-12T... | Current date/time |
View All Facts
# Show all facts for a host
ansible myhost -m setup
# Filter specific facts
ansible myhost -m setup -a "filter=ansible_distribution*"
# Show network facts
ansible myhost -m setup -a "filter=ansible_default_ipv4"
In a playbook:
- name: Dump all facts
ansible.builtin.debug:
var: ansible_facts
- name: Show specific facts
ansible.builtin.debug:
msg:
os: "{{ ansible_distribution }} {{ ansible_distribution_version }}"
ip: "{{ ansible_default_ipv4.address }}"
cpus: "{{ ansible_processor_vcpus }}"
ram_mb: "{{ ansible_memtotal_mb }}"
disk: "{{ ansible_mounts | selectattr('mount', 'equalto', '/') | map(attribute='size_total') | first | human_readable }}"
Using Facts in Conditions
---
- name: OS-specific tasks
hosts: all
tasks:
# Package management by OS family
- name: Install packages (Debian/Ubuntu)
ansible.builtin.apt:
name: "{{ item }}"
state: present
loop:
- nginx
- python3-pip
when: ansible_os_family == "Debian"
- name: Install packages (RHEL/CentOS/Fedora)
ansible.builtin.dnf:
name: "{{ item }}"
state: present
loop:
- nginx
- python3-pip
when: ansible_os_family == "RedHat"
# Version-specific logic
- name: Ubuntu 24.04+ specific config
ansible.builtin.template:
src: ubuntu24.conf.j2
dest: /etc/myapp.conf
when:
- ansible_distribution == "Ubuntu"
- ansible_distribution_major_version | int >= 24
# Architecture check
- name: Download binary for correct architecture
ansible.builtin.get_url:
url: "https://releases.example.com/app-{{ ansible_architecture }}.tar.gz"
dest: /tmp/app.tar.gz
# Memory-based decisions
- name: Configure for high-memory servers
ansible.builtin.set_fact:
db_cache_size: "{{ (ansible_memtotal_mb * 0.7) | int }}m"
when: ansible_memtotal_mb > 4096
- name: Configure for low-memory servers
ansible.builtin.set_fact:
db_cache_size: "512m"
when: ansible_memtotal_mb <= 4096
Facts in Templates
templates/nginx.conf.j2:
# Generated by Ansible for {{ ansible_fqdn }}
# OS: {{ ansible_distribution }} {{ ansible_distribution_version }}
# CPUs: {{ ansible_processor_vcpus }}
worker_processes {{ ansible_processor_vcpus }};
events {
worker_connections {{ (ansible_memtotal_mb / 4) | int }};
}
http {
server {
listen 80;
server_name {{ ansible_fqdn }};
# Access log includes hostname for multi-server setups
access_log /var/log/nginx/{{ ansible_hostname }}-access.log;
}
}
Disable Fact Gathering
Fact gathering adds 1-5 seconds per host. Disable it when you don't need system info:
---
- name: Quick task (no facts needed)
hosts: all
gather_facts: false
tasks:
- name: Restart service
ansible.builtin.service:
name: myapp
state: restarted
Or gather only specific subsets:
---
- name: Gather minimal facts
hosts: all
gather_facts: false
tasks:
- name: Gather only network facts
ansible.builtin.setup:
gather_subset:
- network
- "!all"
- name: Gather everything except hardware (faster)
ansible.builtin.setup:
gather_subset:
- "!hardware"
Available subsets: all, min, hardware, network, virtual, ohai, facter
Custom Facts (Local Facts)
Create custom facts on managed hosts in /etc/ansible/facts.d/:
On the remote host, create /etc/ansible/facts.d/myapp.fact:
[general]
app_version=2.1.0
environment=production
deploy_date=2026-04-12
[database]
host=db.internal
port=5432
Or as a JSON file (/etc/ansible/facts.d/myapp.fact):
{
"app_version": "2.1.0",
"environment": "production"
}
Or as an executable script that outputs JSON:
#!/bin/bash
echo '{"disk_usage": "'$(df -h / | tail -1 | awk '{print $5}')'"}'
Access custom facts:
- name: Use custom facts
ansible.builtin.debug:
msg: "App version: {{ ansible_local.myapp.general.app_version }}"
Deploy Custom Facts with Ansible
---
- name: Deploy custom facts
hosts: all
become: true
tasks:
- name: Create facts directory
ansible.builtin.file:
path: /etc/ansible/facts.d
state: directory
mode: "0755"
- name: Deploy custom fact file
ansible.builtin.template:
src: myapp.fact.j2
dest: /etc/ansible/facts.d/myapp.fact
mode: "0644"
- name: Re-read facts after deploying
ansible.builtin.setup:
filter: ansible_local
Fact Caching
Cache facts to speed up subsequent runs:
# ansible.cfg
[defaults]
gathering = smart # Only gather facts if not cached
fact_caching = jsonfile # Cache backend
fact_caching_connection = /tmp/ansible-facts-cache
fact_caching_timeout = 3600 # Cache for 1 hour (seconds)
Other cache backends: redis, memcached, mongodb, yaml
# Redis caching
[defaults]
fact_caching = redis
fact_caching_connection = localhost:6379:0
fact_caching_timeout = 7200
ansible_hostname vs inventory_hostname
| Variable | Source | Example |
|---|---|---|
ansible_hostname | Gathered from remote host (hostname -s) | web-01 |
ansible_fqdn | Gathered from remote host (hostname -f) | web-01.example.com |
inventory_hostname | From inventory file | web-01.example.com or 10.0.0.5 |
inventory_hostname_short | First part of inventory hostname | web-01 |
# These can differ!
- ansible.builtin.debug:
msg:
ansible_hostname: "{{ ansible_hostname }}" # web-01
inventory_hostname: "{{ inventory_hostname }}" # 10.0.0.5 (if IP in inventory)
Troubleshooting
"ansible_default_ipv4 is undefined"
The host has no default route or network facts weren't gathered:
- name: Gather network facts explicitly
ansible.builtin.setup:
gather_subset:
- network
- name: Use with fallback
ansible.builtin.debug:
msg: "{{ ansible_default_ipv4.address | default('unknown') }}"
Slow Fact Gathering
# Option 1: Disable entirely
gather_facts: false
# Option 2: Gather only what you need
- ansible.builtin.setup:
gather_subset:
- "!all"
- "!min"
- network
# Option 3: Use fact caching (see above)
Custom Facts Not Appearing
Check the fact file on the remote host:
# Must be in /etc/ansible/facts.d/
# Must end in .fact
# Must be valid INI, JSON, or executable returning JSON
# Must be readable (chmod 644 for files, 755 for scripts)
Related Articles
- Ansible setup Module
- Ansible set_fact Module
- Ansible when Conditional
- Ansible Variables Guide
- Ansible register Variables
- Ansible CMDB Host Inventory Reports
Conclusion
Ansible facts give you automatic access to system information — OS, IPs, CPU, memory, disks — without writing any discovery logic. Use them for OS-specific conditionals (ansible_os_family), template generation (ansible_processor_vcpus), and capacity-based decisions (ansible_memtotal_mb). Disable gathering with gather_facts: false for speed, use gather_subset for partial collection, and enable fact caching for large inventories. Custom facts in /etc/ansible/facts.d/ extend the system with your application-specific data.