AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

Ansible set_fact: Create & Set Runtime Variables (Complete Guide)

By Luca Berton · Published 2024-01-01 · Category: linux-administration

How to use ansible.builtin.set_fact to create variables at runtime. Set facts from task output, conditionals, loops, and registered variables.

Ansible set_fact: Create & Set Runtime Variables (Complete Guide)

The ansible.builtin.set_fact module creates or modifies variables during playbook execution. Unlike static variables defined in vars files, set_fact variables are computed at runtime based on task results, conditions, and gathered facts.

See also: Ansible set_fact vs vars vs extra_vars: Variable Types Compared

Basic Usage

---
- name: set_fact examples
  hosts: all
  tasks:
    - name: Set a simple fact
      ansible.builtin.set_fact:
        app_env: production
        app_port: 8080

- name: Use the fact ansible.builtin.debug: msg: "Running {{ app_env }} on port {{ app_port }}"

Set Facts from Registered Output

- name: Get disk usage
  ansible.builtin.shell: df -h / | tail -1 | awk '{print $5}' | tr -d '%'
  register: disk_cmd
  changed_when: false

- name: Set disk usage fact ansible.builtin.set_fact: disk_usage_percent: "{{ disk_cmd.stdout | int }}" disk_is_full: "{{ disk_cmd.stdout | int > 85 }}"

- name: Alert if disk is full ansible.builtin.debug: msg: "WARNING: Disk is {{ disk_usage_percent }}% full!" when: disk_is_full | bool

See also: Ansible include_vars: Load Variables from Files Dynamically (Complete Guide)

Set Facts from Gathered Facts

- name: Derive facts from system info
  ansible.builtin.set_fact:
    is_debian: "{{ ansible_os_family == 'Debian' }}"
    is_rhel: "{{ ansible_os_family == 'RedHat' }}"
    total_memory_gb: "{{ (ansible_memtotal_mb / 1024) | round(1) }}"
    primary_ip: "{{ ansible_default_ipv4.address }}"
    short_hostname: "{{ ansible_hostname }}"

- name: Configure based on OS ansible.builtin.debug: msg: "{{ short_hostname }} — {{ total_memory_gb }}GB RAM — {{ primary_ip }}"

Conditional set_fact

- name: Set environment-specific variables
  ansible.builtin.set_fact:
    db_host: "{{ 'db-prod.example.com' if env == 'production' else 'db-staging.example.com' }}"
    worker_count: "{{ 8 if env == 'production' else 2 }}"
    debug_mode: "{{ env != 'production' }}"

# Or using when conditions: - name: Set production facts ansible.builtin.set_fact: db_host: db-prod.example.com worker_count: 8 when: env == 'production'

- name: Set staging facts ansible.builtin.set_fact: db_host: db-staging.example.com worker_count: 2 when: env == 'staging'

See also: Ansible set_fact Module: Set Variables Dynamically (Complete Guide 2026)

Set Facts with Loops

- name: Build server list from inventory
  ansible.builtin.set_fact:
    web_servers: "{{ groups['webservers'] | map('extract', hostvars, 'ansible_host') | list }}"

- name: Build comma-separated list ansible.builtin.set_fact: allowed_ips: "{{ groups['trusted'] | map('extract', hostvars, 'ansible_default_ipv4') | map(attribute='address') | join(',') }}"

Accumulate Values in a Loop

- name: Initialize list
  ansible.builtin.set_fact:
    healthy_hosts: []

- name: Check each host and accumulate healthy ones ansible.builtin.set_fact: healthy_hosts: "{{ healthy_hosts + [item] }}" loop: "{{ groups['webservers'] }}" when: hostvars[item].health_check.status == 200

Cacheable Facts

By default, set_fact variables only live during the current play. Use cacheable: true to persist them across plays (requires fact caching):

- name: Set cacheable fact
  ansible.builtin.set_fact:
    last_deploy_time: "{{ ansible_date_time.iso8601 }}"
    cacheable: true
# ansible.cfg — enable fact caching
[defaults]
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts_cache
fact_caching_timeout = 86400

Set Facts from API Responses

- name: Get application version from API
  ansible.builtin.uri:
    url: https://api.example.com/version
    return_content: true
  register: api_response

- name: Set version facts ansible.builtin.set_fact: app_version: "{{ (api_response.content | from_json).version }}" app_build: "{{ (api_response.content | from_json).build_number }}"

Set Complex Data Structures

- name: Set dictionary fact
  ansible.builtin.set_fact:
    database_config:
      host: "{{ db_host }}"
      port: 5432
      name: "{{ app_name }}_{{ env }}"
      max_connections: "{{ 200 if env == 'production' else 20 }}"

- name: Set list fact ansible.builtin.set_fact: required_packages: - nginx - postgresql-client - "{{ 'python3-pip' if ansible_os_family == 'Debian' else 'python3-pip' }}"

set_fact vs vars vs register

| Feature | set_fact | vars | register | |---------|----------|------|----------| | When set | During task execution | Before play starts | After task runs | | Dynamic | ✅ Yes | ❌ No (Jinja2 evaluated once) | ✅ Yes | | Scope | Host-level, rest of play | Play/role level | Task-level, rest of play | | Cacheable | ✅ With cacheable: true | ❌ No | ❌ No | | Overrides vars | ✅ Higher precedence | — | ❌ No | | Use case | Computed values | Static config | Task output |

FAQ

What is ansible set_fact?

ansible.builtin.set_fact creates variables at runtime during playbook execution. Unlike vars, set_fact values are computed dynamically and can depend on task results, gathered facts, and conditions.

What is the difference between set_fact and register?

register captures the output of a specific task (stdout, rc, etc.). set_fact creates arbitrary variables with any value. Use register to capture command output, then set_fact to extract and transform what you need.

Do set_fact variables persist across plays?

By default, no — they're available for the rest of the current play only. Use cacheable: true with fact caching enabled to persist across plays and even playbook runs.

Can I use set_fact with loops?

Yes. set_fact executes for each loop iteration. To accumulate values, append to a list: set_fact: my_list: "{{ my_list + [item] }}". Initialize the list first with an empty [].

What precedence does set_fact have?

set_fact has high precedence (level 19 of 22) — it overrides vars, vars_files, group_vars, and role defaults. Only include_params, role params, and extra_vars (-e) override set_fact.

Conclusion

ansible.builtin.set_fact is essential for dynamic playbooks that adapt based on runtime data. Use it to compute variables from command output, API responses, and system facts. Combine with register, conditionals, and loops for powerful automation logic.

Related Articles

Ansible Variables: Complete GuideAnsible register: Capture Task OutputAnsible Magic Variables: Complete ReferenceAnsible debug Module: Print Variables

Category: linux-administration

Browse all Ansible tutorials · AnsiblePilot Home