Ansible Variables, Facts, and Conditionals Explained
Use Ansible variables, auto-gathered facts, and when conditions to tune one playbook so it runs correctly across every host in your fleet.
Netbay Developer Relations
Netbay Engineering
On this page
A playbook that hardcodes values works exactly once. Variables are what turn a single playbook into something you can run across a fleet where hosts differ in distribution, memory, and role. This post covers the three pieces Ansible uses to make that work: variables, facts, and conditionals.
Variables: Where You Define Them
Ansible reads variables from several places, and precedence rules decide who wins when the same name appears twice. From weakest to strongest: role defaults, inventory file groups, inventory host settings, play vars, then variables passed at the command line with -e. The practical rule is simple — put environment-wide defaults in group_vars, put one-off overrides on the ansible-playbook command line, and never fight the precedence ladder by redefining the same variable in five files.
A play can also define vars directly for a single run:
- name: Tune per-host services
hosts: webservers
gather_facts: true
vars:
nginx_port: 8080
health_path: /healthz
tasks:
- name: Write nginx site config
ansible.builtin.template:
src: site.conf.j2
dest: /etc/nginx/sites-enabled/site.conf
notify: reload nginx
handlers:
- name: reload nginx
ansible.builtin.service:
name: nginx
state: reloadedThe template referenced above can then print the port as {{ nginx_port }} inside a Jinja2 file without any shell quoting to worry about.
Facts: What Ansible Already Knows
Before it runs tasks, Ansible walks each host and records facts — the distribution, kernel version, total memory, IP addresses, and dozens more. These live under ansible_facts and let your playbooks adapt without you hardcoding anything. Facts are gathered once per play unless you disable them with gather_facts: false, which matters on very large fleets where gathering is the slowest phase.
ansible webservers -i hosts.ini -m setup --tree facts/
ansible webservers -i hosts.ini -m debug -a 'var=ansible_facts.memtotal_mb'The first command dumps every fact for the group into files, handy for inspection; the second prints one fact to the terminal. Custom facts are just files or scripts dropped into /etc/ansible/facts.d on the host, which is a clean way to let machine-local knowledge (a custom agent version, a license count) flow back into playbooks.
Conditionals with when
The when keyword gates a task on an expression. It reads variables, facts, or the result of a previous task, which is what makes a single playbook safe across heterogeneous hosts:
- name: Start nginx only on Ubuntu nodes
hosts: webservers
tasks:
- name: Start nginx
ansible.builtin.service:
name: nginx
state: started
when: ansible_facts['distribution'] == "Ubuntu"
- name: Add extra swap on small-memory hosts
ansible.builtin.command:
cmd: dd if=/dev/zero of=/swapfile bs=1M count=2048
when:
- ansible_facts['memtotal_mb'] < 2048
- ansible_facts['distribution'] == "Ubuntu"Expressions combine with the usual and, or, and not, and you can reference any registered result, so one play can branch on the outcome of the task before it. Notice the command task above is intentionally a one-shot; later posts in this series show why re-runnable alternatives like the swapfile module are usually the better call.
Takeaway
Master the trio in order: define variables in the weakest sensible place, lean on auto-gathered facts instead of assumptions, and gate everything uncertain behind when. The payoff is a single playbook you can trust across production and staging alike. To get realistic facts to play with, deploy a couple of Ubuntu test nodes on Netbay from the dashboard — netbayhosts.in provisions in under 60 seconds.
Keep reading
Follow along on a real VPS
Deploy Linux in under 60 seconds
These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.
Deploy an instance