Jinja2

Templating engine used by Ansible - variable output, filters, conditionals, loops, and practical config templates.

Jinja2 Basics

Variable output
{{ hostname }}
{{ ansible_default_ipv4.address }}
{{ users | length }}
Comments
{# This is a Jinja2 comment - not rendered #}
Expressions in context (nginx template)
# /etc/nginx/nginx.conf - managed by Ansible
server {
    listen {{ nginx_port | default(80) }};
    server_name {{ inventory_hostname }}.{{ domain }};

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

PATTERN: Templates live in roles/<name>/templates/ with .j2 extension.

Filters

String filters
{{ hostname | upper }}                     {# WEB-01 #}
{{ hostname | lower }}                     {# web-01 #}
{{ hostname | capitalize }}                {# Web-01 #}
{{ hostname | replace('-', '_') }}         {# web_01 #}
{{ "  spaced  " | trim }}                 {# spaced #}
{{ path | basename }}                      {# file.conf #}
{{ path | dirname }}                       {# /etc/app #}
Default values
{{ http_port | default(80) }}
{{ custom_dns | default(omit) }}          {# Omit param entirely if undefined #}
{{ optional_list | default([], true) }}   {# Default even if defined but empty #}
List filters
{{ packages | join(', ') }}
{{ servers | sort }}
{{ servers | unique }}
{{ servers | first }}
{{ servers | last }}
{{ [1, 2, 3] | sum }}
{{ servers | select('match', '^web') | list }}
Type conversion
{{ string_port | int }}
{{ count | string }}
{{ "true" | bool }}
{{ dict_var | to_nice_yaml }}
{{ dict_var | to_nice_json }}
Ansible-specific filters
{{ secret | password_hash('sha512') }}
{{ ip_list | ipaddr('address') }}
{{ 'text' | b64encode }}
{{ encoded | b64decode }}
{{ path | regex_replace('^/old', '/new') }}
{{ list_var | regex_findall('pattern') }}

Control Structures

Conditionals
{% if ansible_os_family == 'RedHat' %}
[rhel-base]
name=RHEL Base
baseurl=https://mirror.example.com/rhel/$releasever/
{% elif ansible_os_family == 'Debian' %}
deb https://mirror.example.com/debian/ {{ ansible_distribution_release }} main
{% endif %}
Loops
{% for user in users %}
{{ user.name }}:x:{{ user.uid }}:{{ user.gid }}::/home/{{ user.name }}:/bin/bash
{% endfor %}
Loop with index and conditionals
{% for host in groups['webservers'] %}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_default_ipv4']['address'] }}:{{ http_port }}{% if loop.first %} check{% endif %}

{% endfor %}
Loop variables
{% for item in list %}
{{ loop.index }}      {# 1-indexed #}
{{ loop.index0 }}     {# 0-indexed #}
{{ loop.first }}      {# True on first iteration #}
{{ loop.last }}       {# True on last iteration #}
{{ loop.length }}     {# Total items #}
{% endfor %}

Practical Templates

HAProxy backend config
# Managed by Ansible - do not edit
backend app_servers
    balance roundrobin
{% for host in groups['appservers'] %}
    server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_default_ipv4']['address'] }}:{{ app_port | default(8080) }} check
{% endfor %}
SSH config template
# {{ ansible_managed }}
{% for host in managed_hosts %}
Host {{ host.name }}
    HostName {{ host.ip }}
    User {{ host.user | default('ansible') }}
    IdentityFile {{ ssh_key_path | default('~/.ssh/id_ed25519') }}
{% if host.jump is defined %}
    ProxyJump {{ host.jump }}
{% endif %}

{% endfor %}
systemd unit template
# {{ ansible_managed }}
[Unit]
Description={{ service_description }}
After=network.target
{% if service_requires is defined %}
Requires={{ service_requires | join(' ') }}
{% endif %}

[Service]
Type={{ service_type | default('simple') }}
User={{ service_user }}
ExecStart={{ service_exec }}
{% for key, value in service_env.items() %}
Environment="{{ key }}={{ value }}"
{% endfor %}
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

PATTERN: Always include # {{ ansible_managed }} at the top of generated configs.

Quick Reference

Pattern Description

{{ variable }}

Output variable value

{% if condition %}…​{% endif %}

Conditional block

{% for item in list %}…​{% endfor %}

Loop iteration

{{ var | default('fallback') }}

Default filter for undefined vars

{{ list | join(', ') }}

Join list elements

{{ dict | to_nice_yaml }}

Convert dict to YAML (Ansible)

{% set myvar = 'value' %}

Set a variable

{{ var | regex_replace('^old', 'new') }}

Regex substitution (Ansible)

{{ groups['webservers'] }}

Access inventory group members

{{ hostvars[host]['ansible_default_ipv4']['address'] }}

Cross-host variable access