Ansible Facts — ansible_facts
Every playbook you’ve run has started with an unexplained task: TASK [Gathering Facts]. This chapter finally explains what that is, where it comes from, and how to actually use what it collects. You can even disable it.
What Are Facts?
Facts are variables Ansible automatically discovers about each managed host — its operating system, network addresses, hostname, memory, and a long list of other system details. Unlike everything else in this section so far, you don’t define facts yourself; Ansible collects them fresh, per host, at the start of a play.
Where Do Facts Come From?
That “Gathering Facts” task is a real module, ansible.builtin.setup, running implicitly at the start of every play unless you turn it off. It connects to each host, inspects the system, and stores what it finds as variables available for the rest of that run — under ansible_facts.
Printing A Fact
- name: Facts Demo
hosts: servers
tasks:
- name: Print the OS family
ansible.builtin.debug:
msg: "{{ ansible_facts['os_family'] }}"ok: [ubuntu] => {
"msg": "Debian"
}
ok: [fedora] => {
"msg": "RedHat"
}Same task, same variable name, genuinely different values — because facts are collected fresh per host, and our two lab machines actually are different distributions.
Common Facts Worth Knowing
| Fact | Holds |
|---|---|
ansible_facts['os_family'] | Broad OS family (Debian, RedHat, and so on) |
ansible_facts['distribution'] | Specific distribution name |
ansible_facts['hostname'] | The host’s own hostname |
ansible_facts['default_ipv4']['address'] | Primary IPv4 address |
ansible_facts['memtotal_mb'] | Total memory, in megabytes |
Note
You’ll also see some facts referenced directly as ansible_hostname, ansible_distribution, and so on — without the ansible_facts['...'] lookup. This shorthand exists for backward compatibility and still works, but ansible_facts['...'] is the current, explicit, recommended form. Expect to see both in the wild.
Disabling Fact Gathering
If a play doesn’t need any system information at all, gather_facts: false skips the automatic setup task entirely, saving a little time on every run:
- name: No Facts Needed
hosts: servers
gather_facts: false
tasks:
- name: Just print a static message
ansible.builtin.debug:
msg: "This play never touched facts"Not something to reach for by default — just an option worth knowing exists once you notice a play genuinely never references any fact.
A Gotcha: Bracket Notation Is Safer Than Dot Notation
You may see facts accessed two ways: ansible_facts.os_family and ansible_facts['os_family']. Both often work — but dot notation can silently break for keys that clash with Python’s own dictionary method names, or contain characters dot notation can’t represent cleanly. Bracket notation, ansible_facts['os_family'], always works, unambiguously. Make it the default habit rather than something you only reach for when dot notation happens to fail.
Best Practices
- Leave fact gathering enabled unless you have a specific, deliberate reason to disable it — the cost is small, and the information is frequently useful even when you didn’t plan to need it upfront.
- Use bracket notation (
ansible_facts['key']) by default, not the dot-shorthand. - Print facts to explore what’s available before hardcoding a value. If you’re about to hardcode an OS-specific path or package name, check whether a fact could tell you the right value dynamically instead.