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 defaults — a | 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 Jinja2 • Ansible set_fact Module: Set Variables Dynamically • Ansible Extra Vars: Pass Variables from Command Line • Ansible Filters: Transform Data in PlaybooksCategory: installation