Ansible when Conditional: Complete Guide with Examples
By Luca Berton · Published 2026-04-03 · Category: installation
Complete guide to Ansible when conditionals. Use when statements with facts, variables, registered results, and complex boolean logic in playbooks.
The when statement is Ansible's conditional — it controls whether a task runs based on variables, facts, registered results, or any Jinja2 expression.
Basic when Syntax
- name: Install nginx on Debian
ansible.builtin.apt:
name: nginx
state: present
when: ansible_os_family == "Debian"
Important: when uses raw Jinja2 expressions — do NOT wrap in {{ }}.
See also: Ansible Loops: Complete Guide with loop, with_items & Examples
Boolean Conditions
# Variable is true
- debug: msg="Debug mode"
when: debug_mode
# Variable is false
- debug: msg="Production mode"
when: not debug_mode
# Variable equals value
- debug: msg="Version 2"
when: app_version == "2.0"
# Numeric comparison
- debug: msg="Low disk"
when: disk_free_gb < 10
OS and Distribution Conditions
# By OS family
- name: Install on RedHat family
ansible.builtin.dnf:
name: httpd
state: present
when: ansible_os_family == "RedHat"
# By distribution
- name: Install on Ubuntu only
ansible.builtin.apt:
name: nginx
state: present
when: ansible_distribution == "Ubuntu"
# By version
- name: Only on Ubuntu 24.04+
ansible.builtin.apt:
name: nginx
state: present
when:
- ansible_distribution == "Ubuntu"
- ansible_distribution_major_version | int >= 24
See also: Ansible ternary Filter: Inline If-Else Conditional in Jinja2 (Guide)
AND / OR Logic
AND (all conditions must be true)
# Method 1: List (implicit AND)
- name: Install on Ubuntu production servers
ansible.builtin.apt:
name: nginx
when:
- ansible_distribution == "Ubuntu"
- env == "production"
- ansible_memtotal_mb >= 2048
# Method 2: Explicit AND
- name: Same thing with 'and'
ansible.builtin.apt:
name: nginx
when: ansible_distribution == "Ubuntu" and env == "production"
OR (any condition)
- name: Install on Debian or Ubuntu
ansible.builtin.apt:
name: nginx
when: ansible_distribution == "Debian" or ansible_distribution == "Ubuntu"
Complex combinations
- name: Complex condition
ansible.builtin.debug:
msg: "Condition met"
when: >-
(ansible_distribution == "Ubuntu" and ansible_distribution_major_version | int >= 22) or
(ansible_distribution == "Debian" and ansible_distribution_major_version | int >= 12)
Variable Testing
# Variable is defined
- debug: msg="{{ my_var }}"
when: my_var is defined
# Variable is not defined
- debug: msg="Using default"
when: custom_port is not defined
# Variable is not empty
- debug: msg="{{ my_var }}"
when: my_var | length > 0
# Variable is in a list
- debug: msg="Valid environment"
when: env in ['production', 'staging', 'development']
# Variable contains string
- debug: msg="Found keyword"
when: "'error' in log_output"
See also: Ansible check_mode: Dry Run & Test Playbooks Without Making Changes
Registered Variables
- name: Check if file exists
ansible.builtin.stat:
path: /etc/myapp/config.yml
register: config_file
- name: Create config if missing
ansible.builtin.template:
src: config.yml.j2
dest: /etc/myapp/config.yml
when: not config_file.stat.exists
- name: Check service status
ansible.builtin.command: systemctl is-active myapp
register: service_status
ignore_errors: true
- name: Start service if not running
ansible.builtin.service:
name: myapp
state: started
when: service_status.rc != 0
when with Loops
- name: Install only packages that aren't installed
ansible.builtin.apt:
name: "{{ item.name }}"
state: present
loop:
- { name: nginx, install: true }
- { name: apache2, install: false }
- { name: postgresql, install: true }
when: item.install
when with Blocks
- name: RedHat-specific tasks
when: ansible_os_family == "RedHat"
block:
- name: Install EPEL
ansible.builtin.dnf:
name: epel-release
state: present
- name: Install packages
ansible.builtin.dnf:
name: "{{ item }}"
state: present
loop:
- nginx
- postgresql-server
Test Filters
# String tests
when: my_var is match("^web\d+")
when: my_var is search("error")
when: my_var is regex("^[a-z]+$")
# Type tests
when: my_var is string
when: my_var is number
when: my_var is mapping # dict
when: my_var is iterable
# Comparison tests
when: my_version is version('2.0', '>=')
when: path is file
when: path is directory
when: path is link
Common Patterns
Skip task in check mode
- name: Run only in real mode
ansible.builtin.command: /opt/deploy.sh
when: not ansible_check_mode
First run detection
- name: Check if already configured
ansible.builtin.stat:
path: /opt/app/.configured
register: configured
- name: Run initial setup
ansible.builtin.command: /opt/app/setup.sh
when: not configured.stat.exists
- name: Mark as configured
ansible.builtin.file:
path: /opt/app/.configured
state: touch
when: not configured.stat.exists
FAQ
Why can't I use {{ }} in when statements?
when already evaluates as a Jinja2 expression. Adding {{ }} would double-evaluate and cause errors. Just use variable names directly.
How do I check if a variable is true/false?
when: my_var checks truthiness (non-empty, non-zero, not None). when: my_var == true checks for boolean True specifically.
Can I use when with include_tasks?
Yes. The condition applies to the include itself — all tasks in the included file will be skipped if the condition is false.
Basic when
- name: Install on Debian only
ansible.builtin.apt:
name: nginx
state: present
when: ansible_os_family == "Debian"
become: true
Common Conditions
# OS family
when: ansible_os_family == "RedHat"
when: ansible_distribution == "Ubuntu"
when: ansible_distribution_version == "24.04"
# Architecture
when: ansible_architecture == "x86_64"
# Variable defined/undefined
when: my_var is defined
when: my_var is not defined
# Boolean
when: enable_feature
when: not disable_logging
when: enable_ssl | bool
# String checks
when: env == "production"
when: "'error' in command_output.stdout"
when: app_version is version('2.0', '>=')
Multiple Conditions (AND)
# All must be true (implicit AND)
- apt: name=nginx
when:
- ansible_os_family == "Debian"
- ansible_distribution_version is version('20.04', '>=')
- env == "production"
OR Conditions
- yum: name=httpd
when: ansible_distribution == "CentOS" or ansible_distribution == "Rocky"
# Complex
- debug: msg="Install web server"
when: >
(ansible_distribution == "Ubuntu" and ansible_distribution_version == "24.04")
or
(ansible_distribution == "Rocky" and ansible_distribution_major_version == "9")
Based on Registered Results
- command: systemctl is-active nginx
register: nginx_status
ignore_errors: true
changed_when: false
- debug: msg="Nginx is running"
when: nginx_status.rc == 0
- debug: msg="Nginx is stopped"
when: nginx_status.rc != 0
# Check stdout
- command: cat /etc/os-release
register: os_info
changed_when: false
- debug: msg="Running Debian"
when: "'Debian' in os_info.stdout"
Check Variable Content
# Empty check
when: my_list | length > 0
when: my_string | length > 0
when: my_var != ""
when: my_var is not none
# In list
when: inventory_hostname in groups['webservers']
when: "'admin' in user_roles"
# Numeric comparison
when: disk_usage | int > 80
when: ansible_memfree_mb > 1024
Conditionals with Loops
- name: Install only needed packages
apt:
name: "{{ item.name }}"
loop:
- { name: nginx, install: true }
- { name: apache2, install: false }
- { name: curl, install: true }
when: item.install
become: true
Block-Level Conditionals
- block:
- apt: name=nginx state=present
- template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
- service: name=nginx state=started enabled=true
when: ansible_os_family == "Debian"
become: true
Version Comparison
when: ansible_distribution_version is version('22.04', '>=')
when: app_version is version('3.0.0', '<')
when: ansible_python_version is version('3.8', '>=')
# Strict version comparison
when: pkg_version is version('2.0.0', '>=', strict=true)
File/Path Tests
- stat: { path: /opt/myapp/config.yml }
register: config
- template: src=config.yml.j2 dest=/opt/myapp/config.yml
when: not config.stat.exists
Common Mistakes
# WRONG: Don't use {{ }} in when
when: "{{ my_var }} == 'value'"
# CORRECT: when already templates
when: my_var == 'value'
# WRONG: Bare variable might be string "false"
when: enable_feature
# CORRECT: Force boolean
when: enable_feature | bool
FAQ
How do I negate a condition?
when: not (ansible_os_family == "Debian")
when: my_var is not defined
when: my_var != "value"
Can I use when with handlers?
Yes — handlers support when, but they only run if notified AND the condition is true.
How do I skip an entire role?
roles:
- role: nginx
when: install_nginx | default(true) | bool
Basic when
- apt: { name: nginx }
when: ansible_os_family == "Debian"
become: true
- yum: { name: nginx }
when: ansible_os_family == "RedHat"
become: true
Multiple Conditions (AND)
- debug:
msg: "Production Ubuntu server"
when:
- ansible_distribution == "Ubuntu"
- env == "production"
- ansible_memtotal_mb >= 4096
OR Conditions
- debug:
msg: "Debian-based"
when: ansible_distribution == "Ubuntu" or ansible_distribution == "Debian"
# With 'in'
- debug:
msg: "Supported distro"
when: ansible_distribution in ["Ubuntu", "Debian", "CentOS", "Rocky"]
Check Variable Defined
- debug:
msg: "Using custom port {{ custom_port }}"
when: custom_port is defined
- debug:
msg: "Using default port"
when: custom_port is not defined
Registered Results
- command: which docker
register: docker_check
ignore_errors: true
changed_when: false
- debug:
msg: "Docker is installed"
when: docker_check.rc == 0
- debug:
msg: "Docker NOT found"
when: docker_check.rc != 0
Boolean Variables
- service:
name: nginx
state: started
when: enable_webserver # true/false
- service:
name: nginx
state: stopped
when: not enable_webserver
Conditional on Facts
# OS version
when: ansible_distribution_version is version('22.04', '>=')
# Architecture
when: ansible_architecture == "x86_64"
# Memory
when: ansible_memtotal_mb > 2048
# Disk space
when: ansible_mounts | selectattr('mount', 'eq', '/') | map(attribute='size_available') | first > 5368709120
Conditional with Loops
- debug:
msg: "Installing {{ item.name }}"
loop:
- { name: nginx, install: true }
- { name: apache2, install: false }
when: item.install
Conditional Blocks
- block:
- apt: { name: nginx }
- template: { src: nginx.conf.j2, dest: /etc/nginx/nginx.conf }
- service: { name: nginx, state: started }
when: ansible_os_family == "Debian"
become: true
String Tests
when: my_string | length > 0 # Not empty
when: my_string is match("^web-.*") # Regex match
when: "'error' in log_output.stdout" # Contains
when: my_var is string # Type check
FAQ
when vs failed_when vs changed_when?
when controls if a task runs. failed_when controls if a task is considered failed. changed_when controls if a task reports as changed.
Can I use Jinja2 in when?
when already evaluates as Jinja2 — don't wrap in {{ }}. Wrong: when: "{{ x }}". Right: when: x.
How to negate a condition?
Use not: when: not my_var or when: my_var is not defined.
Related Articles
• rendering Jinja2 templates with Ansible • Ansible Ignore Errors Guide • Ansible Check Mode Guide • subelements lookup with Ansible loops • reverse proxy with Ansible NginxCategory: installation