Stage 4 · Provision
Playbooks & Variables
Jinja2 Templates
Templating config files with filters, loops, and facts.
The Template Module
The template module copies a Jinja2 template from the control node to managed nodes, rendering variables into the output. Templates live in a templates/ directory alongside your playbook or role.
- name: Deploy nginx config
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: "0644"
backup: yes
notify: Restart nginxThe template module is idempotent — it only reports changed if the rendered content differs from the destination file.
Jinja2 Syntax
Jinja2 uses double curly braces for variable substitution and percent signs for control structures. Ansible templates are Jinja2 with access to all Ansible variables and facts.
# {{ ansible_managed }}
# This file is managed by Ansible. Do not edit manually.
worker_processes {{ nginx_worker_processes | default('auto') }};
events {
worker_connections {{ nginx_worker_connections | default(1024) }};
}
http {
server {
listen {{ http_port | default(80) }};
server_name {{ server_name }};
location / {
proxy_pass http://127.0.0.1:{{ app_port }};
}
}
}{{ ansible_managed }} is automatically replaced with a comment showing who managed the file and when.
Common Filters
Jinja2 filters transform variables. They are chained with the pipe character. Ansible adds many filters beyond the standard Jinja2 set.
# Default value
{{ nginx_port | default(80) }}
# Uppercase
{{ app_name | upper }}
# JSON formatting
{{ config_dict | to_nice_json }}
# YAML formatting
{{ config_dict | to_nice_yaml }}
# File path basename
{{ '/etc/nginx/nginx.conf' | basename }}
# Combine lists
{{ groups['webservers'] | join(', ') }}
# Hash for cache busting
{{ content | hash('sha256') }}
# Regex replace
{{ hostname | regex_replace('^web', 'app') }}Filters are powerful for transforming data in templates. Chain them for complex transformations.
Use the debug module with filters to test: ansible all -m debug -a 'msg={{ variable | default("N/A") }}'. This lets you verify filter behavior before adding to templates.
Conditionals in Templates
{% if enable_ssl %}
server {
listen 443 ssl;
ssl_certificate {{ ssl_cert_path }};
ssl_key {{ ssl_key_path }};
}
{% else %}
server {
listen 80;
}
{% endif %}
{% if environment == 'production' %}
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
{% else %}
access_log /dev/null;
{% endif %}Conditionals let you customize config files per environment without separate templates for each case.
Loops in Templates
{# List of upstream servers #}
upstream backend {
{% for server in backend_servers %}
server {{ server.host }}:{{ server.port }} weight={{ server.weight | default(1) }};
{% endfor %}
}
{# Iterate over dictionary #}
{% for key, value in app_config.items() %}
{{ key }} = {{ value }}
{% endfor %}Loops generate repeated config sections from variable lists. This eliminates duplication when managing server pools or feature flags.
Jinja2 whitespace can cause unexpected blank lines. Use {%- -%} with hyphens to trim whitespace: {% for item in list -%}\n{{ item }}\n{%- endfor %}
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.