Introduction

Jinja2 inline conditionals let you set variables dynamically in Ansible — choosing between values based on conditions, all within a single expression. The most common pattern is the ternary (if-else) expression, but there are several approaches depending on your use case.

The Ternary Pattern

Basic Syntax

# Jinja2 inline if-else
variable: "{{ 'value_a' if condition else 'value_b' }}"

Kubernetes Service Type Example

# Toggle between NodePort and ClusterIP
service_type: "{{ 'NodePort' if expose_service else 'ClusterIP' }}"

The Boolean Trap

The Problem

# BROKEN — compares boolean true to string 'true'
service_type: "{{ 'NodePort' if expose_service == 'true' else 'ClusterIP' }}"

When expose_service is a boolean true, comparing it to the string 'true' fails silently — the condition evaluates to false, and you always get ClusterIP.

The Fix: Use | bool

# CORRECT — works with bool true, string 'true', 'yes', '1'
service_type: "{{ 'NodePort' if expose_service | bool else 'ClusterIP' }}"

The | bool filter normalizes any input to a proper boolean:

| Input | | bool Result | |-------|----------------| | true | True | | 'true' | True | | 'yes' | True | | '1' | True | | 1 | True | | false | False | | 'false' | False | | 'no' | False | | '0' | False | | 0 | False |

The Ternary Filter

Ansible provides a dedicated ternary filter as an alternative:

# Using the ternary filter
service_type: "{{ expose_service | bool | ternary('NodePort', 'ClusterIP') }}"

# With a third value for undefined/null
debug_mode: "{{ enable_debug | ternary('verbose', 'quiet', 'default') }}"

Ternary vs Inline If-Else

# These are equivalent:
a: "{{ 'yes' if condition else 'no' }}"
b: "{{ condition | ternary('yes', 'no') }}"

The ternary filter is cleaner when chaining with other filters.

Playbook workflow

Generate a complete playbook from this tutorial

Use Ansible Jinja2 Conditionals — Ternary as the starting context and generate an editable Ansible playbook in the playground.

One anonymous generation is available. Do not paste passwords, private keys, tokens, or customer secrets.

Generate a playbook

Common Patterns

Default Values

# Use default when variable might be undefined
port: "{{ custom_port | default(8080) }}"

# Default with boolean check
log_level: "{{ 'debug' if verbose | default(false) | bool else 'info' }}"

Nested Conditions

# Multi-level selection
env_name: "{{ 'production' if is_prod | bool else ('staging' if is_staging | bool else 'development') }}"

Conditional List Items

# Include items conditionally
packages: >-
  {{ ['nginx', 'curl'] +
     (['redis'] if use_cache | bool else []) +
     (['postgresql-client'] if use_db | bool else []) }}

String Manipulation with Conditions

# Add suffix conditionally
hostname: "{{ base_name }}{{ '-dev' if env == 'development' else '' }}"

# Select from mapping
region_endpoint: "{{ {'us': 'us-east-1', 'eu': 'eu-west-1', 'ap': 'ap-southeast-1'}[region] | default('us-east-1') }}"

Practical Examples

Kubernetes Deployment

vars:
  expose_service: true
  replicas_count: "{{ 3 if env == 'production' else 1 }}"
  resource_limits: "{{ '2Gi' if env == 'production' else '512Mi' }}"
  image_tag: "{{ 'latest' if env == 'development' else version }}"
  service_type: "{{ expose_service | bool | ternary('NodePort', 'ClusterIP') }}"

Platform-Specific Configuration

- name: Set package manager command
  ansible.builtin.set_fact:
    pkg_manager: "{{ 'dnf' if ansible_os_family == 'RedHat' else 'apt' }}"
    service_name: "{{ 'httpd' if ansible_os_family == 'RedHat' else 'apache2' }}"
    config_path: "{{ '/etc/httpd/conf/httpd.conf' if ansible_os_family == 'RedHat' else '/etc/apache2/apache2.conf' }}"

Feature Flags

vars:
  features:
    ssl: true
    monitoring: false
    cache: true

tasks:
  - name: Configure features
    ansible.builtin.template:
      src: config.j2
      dest: /etc/myapp/config.yml
    vars:
      ssl_enabled: "{{ features.ssl | bool | ternary('on', 'off') }}"
      monitoring_endpoint: "{{ 'http://monitor:9090' if features.monitoring | bool else omit }}"
      cache_backend: "{{ 'redis' if features.cache | bool else 'memory' }}"

Conditional when vs Ternary

# Use 'when' for skipping entire tasks
- name: Install Redis
  ansible.builtin.dnf:
    name: redis
    state: present
  when: use_cache | bool

# Use ternary for choosing between values within a task
- name: Configure cache backend
  ansible.builtin.lineinfile:
    path: /etc/myapp/config.yml
    regexp: '^cache_backend'
    line: "cache_backend: {{ use_cache | bool | ternary('redis', 'memory') }}"

Common Mistakes

Comparing Booleans to Strings

# WRONG
result: "{{ 'yes' if my_var == 'true' else 'no' }}"

# CORRECT
result: "{{ 'yes' if my_var | bool else 'no' }}"

Missing Quotes Around Jinja2 Expressions

# WRONG — YAML interprets { as a mapping
result: {{ 'a' if true else 'b' }}

# CORRECT — always quote Jinja2 expressions
result: "{{ 'a' if true else 'b' }}"

Undefined Variables

# WRONG — fails if my_var is undefined
result: "{{ 'yes' if my_var else 'no' }}"

# CORRECT — use default filter
result: "{{ 'yes' if my_var | default(false) | bool else 'no' }}"

Conclusion

Use | bool whenever comparing variables that might be strings or booleans. Prefer the ternary filter for clean, chainable expressions, and | default() for undefined variables. The pattern {{ variable | default(false) | bool | ternary('value_a', 'value_b') }} handles every edge case — undefined, string, or boolean input.