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 default() Filter: Set Fallback Values for Undefined Variables

By Luca Berton · Published 2024-01-01 · Category: installation

Complete guide to the Ansible default filter. Set fallback values for undefined variables, handle empty strings, omit parameters conditionally, and chain.

The default filter (also written as d) is one of the most important Jinja2 filters in Ansible. It provides a fallback value when a variable is undefined, preventing the dreaded "AnsibleUndefinedVariable" error and making your playbooks robust and reusable.

Basic Syntax

{{ variable | default(fallback_value) }}
# Shorthand
{{ variable | d(fallback_value) }}

If variable is defined, its value is used. If undefined, fallback_value is returned.

- name: Use default value for undefined variable
  ansible.builtin.debug:
    msg: "Port is {{ http_port | default(8080) }}"
  # If http_port is undefined → "Port is 8080"
  # If http_port is 3000 → "Port is 3000"

See also: Ansible map Filter: Extract Attributes from Lists (Complete Guide)

Handle Undefined Variables

Simple Fallback

vars:
  app_name: myapp
  # app_port is NOT defined

tasks: - name: Show application config ansible.builtin.debug: msg: | App: {{ app_name }} Port: {{ app_port | default(8080) }} Env: {{ app_env | default('production') }} Workers: {{ app_workers | default(4) }}

Fallback to Another Variable

- name: Use primary or secondary DNS
  ansible.builtin.debug:
    msg: "DNS: {{ custom_dns | default(company_dns) | default('8.8.8.8') }}"

This chains defaults: try custom_dns, then company_dns, then '8.8.8.8'.

default(true) — Handle Empty and False Values

By default, the default filter only catches undefined variables. Defined-but-empty values pass through:

vars:
  my_var: ""  # empty string — defined!

tasks: - name: Default does NOT catch empty strings ansible.builtin.debug: msg: "{{ my_var | default('fallback') }}" # Output: "" (empty string, NOT 'fallback')

Add true as the second parameter to also catch empty/falsy values:

- name: Catch empty, false, null, 0, and undefined
  ansible.builtin.debug:
    msg: "{{ my_var | default('fallback', true) }}"
  # Output: "fallback" (because empty string is falsy)

What Counts as Falsy?

| Value | default() | default(x, true) | |-------|-------------|---------------------| | undefined | ✅ Uses fallback | ✅ Uses fallback | | "" (empty string) | ❌ Returns "" | ✅ Uses fallback | | 0 | ❌ Returns 0 | ✅ Uses fallback | | false | ❌ Returns false | ✅ Uses fallback | | null / None | ❌ Returns None | ✅ Uses fallback | | [] (empty list) | ❌ Returns [] | ✅ Uses fallback | | {} (empty dict) | ❌ Returns {} | ✅ Uses fallback |

See also: Ansible regex_replace Filter: Find & Replace with Regex (Complete Guide)

default with omit — Conditional Parameters

The special omit variable tells Ansible to skip a module parameter entirely:

- name: Create user with optional home directory
  ansible.builtin.user:
    name: "{{ item.name }}"
    home: "{{ item.home | default(omit) }}"
    shell: "{{ item.shell | default('/bin/bash') }}"
    groups: "{{ item.groups | default(omit) }}"
  loop:
    - name: alice
      home: /opt/alice
      groups: admin,developers
    - name: bob
      # home and groups not specified — parameters are omitted

Common omit Patterns

# Package version: install specific version or latest
- name: Install package
  ansible.builtin.package:
    name: "{{ pkg_name }}"
    version: "{{ pkg_version | default(omit) }}"
    state: present

# Service: only set enabled if explicitly specified - name: Manage service ansible.builtin.service: name: nginx state: started enabled: "{{ nginx_enabled | default(omit) }}"

# File: only set owner/group if specified - name: Deploy config file ansible.builtin.template: src: app.conf.j2 dest: /etc/app/app.conf owner: "{{ config_owner | default(omit) }}" group: "{{ config_group | default(omit) }}" mode: "{{ config_mode | default('0644') }}"

Use in Conditionals

- name: Run only if feature is enabled
  ansible.builtin.debug:
    msg: "Feature X is active"
  when: enable_feature_x | default(false)

- name: Skip if explicitly disabled ansible.builtin.debug: msg: "Running backup" when: skip_backup | default(false) | bool == false

See also: Ansible Variable Precedence: Complete Order of Priority (2026)

Use in Templates (Jinja2)

# nginx.conf.j2
server {
    listen {{ nginx_port | default(80) }};
    server_name {{ server_name | default('_') }};

{% if ssl_cert | default('') != '' %} ssl_certificate {{ ssl_cert }}; ssl_certificate_key {{ ssl_key | default(ssl_cert | regex_replace('\\.crt$', '.key')) }}; {% endif %}

root {{ document_root | default('/var/www/html') }}; }

Use with Loops and Lists

- name: Configure servers with defaults
  ansible.builtin.template:
    src: vhost.j2
    dest: "/etc/nginx/sites-available/{{ item.name }}"
  loop:
    - name: app1
      port: 8080
      workers: 4
    - name: app2
      # port and workers will use defaults in template
  vars:
    default_port: 80
    default_workers: 2

# In the template: # listen {{ item.port | default(default_port) }}; # worker_processes {{ item.workers | default(default_workers) }};

Use with set_fact

- name: Set configuration with defaults
  ansible.builtin.set_fact:
    final_config:
      database_host: "{{ db_host | default('localhost') }}"
      database_port: "{{ db_port | default(5432) }}"
      database_name: "{{ db_name | default('app_' + app_env | default('production')) }}"
      max_connections: "{{ db_max_conn | default(100) }}"
      ssl_mode: "{{ db_ssl | default('prefer') }}"

Combine with Other Filters

# default + bool
- name: Parse boolean with default
  ansible.builtin.debug:
    msg: "Debug: {{ debug_mode | default('false') | bool }}"

# default + int - name: Parse integer with default ansible.builtin.debug: msg: "Workers: {{ worker_count | default('4') | int }}"

# default + split - name: Parse list from string or use default ansible.builtin.debug: msg: "{{ allowed_hosts | default('localhost') | split(',') }}"

# default + ternary - name: Conditional with fallback ansible.builtin.debug: msg: "{{ (use_ssl | default(false)) | ternary('https', 'http') }}://{{ hostname }}"

Real-World Role Example

# roles/webapp/defaults/main.yml
webapp_port: 8080
webapp_workers: 4
webapp_env: production
webapp_log_level: info

# roles/webapp/tasks/main.yml - name: Deploy web application ansible.builtin.template: src: webapp.conf.j2 dest: /etc/webapp/config.yml mode: '0644' vars: config: port: "{{ webapp_port | default(8080) }}" workers: "{{ webapp_workers | default(4) }}" env: "{{ webapp_env | default('production') }}" log_level: "{{ webapp_log_level | default('info') }}" secret_key: "{{ webapp_secret | default(omit) }}"

Common Mistakes

Mistake 1: Forgetting default(true) for Empty Strings

# Bug: empty string passes through
msg: "{{ user_email | default('admin@example.com') }}"
# If user_email="" → outputs "" (not the default)

# Fix: use default(value, true) msg: "{{ user_email | default('admin@example.com', true) }}"

Mistake 2: Using default with Registered Variables

# Registered variables are always defined (even on failure)
- name: Check service
  ansible.builtin.command: systemctl status nginx
  register: nginx_status
  ignore_errors: true

# Wrong — nginx_status is always defined - debug: msg: "{{ nginx_status | default('unknown') }}"

# Correct — check the actual content - debug: msg: "{{ nginx_status.rc | default(1) }}"

FAQ

What does the default filter do in Ansible?

The default filter provides a fallback value when a variable is undefined. {{ my_var | default('fallback') }} returns 'fallback' if my_var doesn't exist, preventing AnsibleUndefinedVariable errors.

What is the difference between default() and default(true)?

default(value) only catches undefined variables. default(value, true) also catches falsy values: empty strings, 0, false, None, empty lists, and empty dicts. Use true when you want to treat empty values the same as undefined.

How do I use omit with the default filter?

{{ var | default(omit) }} tells Ansible to skip the module parameter entirely if var is undefined. This is useful for optional parameters — the module uses its own built-in default instead.

Can I chain multiple default filters?

Yes: {{ a | default(b) | default(c) | default('last_resort') }}. Ansible tries each value left to right and uses the first one that's defined (or the last fallback).

What is the shorthand for the default filter?

Use d instead of default: {{ my_var | d('fallback') }} is equivalent to {{ my_var | default('fallback') }}.

Conclusion

The default filter is essential for writing robust Ansible playbooks: • default(value) — Fallback for undefined variables • default(value, true) — Fallback for undefined AND empty/falsy values • default(omit) — Skip optional module parameters • Chain defaultsa | default(b) | default(c) for cascading fallbacks

Use it everywhere you reference variables that might not be defined. Your playbooks will be more portable, reusable, and error-resistant.

Related Articles

Ansible Ternary Filter: Inline Conditionals in Jinja2Ansible set_fact Module: Set Variables DynamicallyAnsible Extra Vars: Pass Variables from Command LineAnsible Filters: Transform Data in Playbooks

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home