Welcome to this hands-on guide designed for Ansible learners who want to understand and apply Jinja2 templates effectively. This post explains:
- What Jinja2 is and what it can do (with clear examples)
- How to deploy dynamic configuration files like MOTD and SSHD using Jinja2
- How to use filters to process and clean up data
- Side-by-side examples showing template file, variable YAML file, and the deployed result
Let’s dive in and demystify Jinja2!
What is Jinja2 Template in Ansible?
Jinja2 is the templating engine that powers Ansible’s ability to dynamically generate files such as configuration files, scripts, or banners. Instead of writing one static file per server, we can write one smart template and customize its content depending on the host, group, or environment using variables.
In real-world scenarios, most configuration files are not 100% identical across machines:
- Web servers may use one port, database servers another.
- Hostnames, environment flags, allowed IPs, even login banners might differ.
Manually maintaining a different config file for every server is painful and error-prone. With Jinja2 templates, you only maintain a single template, and Ansible fills in the blanks depending on each host’s variable values.
This approach is:
- ✅ Flexible: You can use conditions and loops
- ✅ Reusable: One template can work for many different hosts
- ✅ Safe: Less room for human error
- ✅ Efficient: Easy to update, easy to read, easy to scale
In the next section, let’s explore what you can actually do with Jinja2 and how to use it through simple, real-life examples.
What Can Jinja2 Do? (With Examples)
Below are common tasks Jinja2 helps with — each paired with:
- ✅ A Jinja2 Template snippet
- 📄 A YAML variable file
- 📥 The final result on the managed host
1. Injecting Variables into a File
Use case: Replace hardcoded values with dynamic variables.
✅ Jinja2 Template (nginx.conf.j2):
server_name {{ inventory_hostname }};
listen {{ nginx_port }};
📄 Variable File (host_vars/web01.yml):
nginx_port: 8080
📥 Deployed Output on web01:
server_name web01;
listen 8080;
2. Using If-Else Conditions
Use case: Customize config depending on environment.
✅ Jinja2 Template (sshd_config):
{% if env == 'staging' %}
PermitRootLogin yes
{% else %}
PermitRootLogin no
{% endif %}
📄 YAML:
env: production
📥 Output:
PermitRootLogin no
3. Looping Over a List
Use case: Add multiple IPs or users.
✅ Template (sshd_config):
{% for ip in allowed_ips %}
allow {{ ip }};
{% endfor %}
📄 YAML:
allowed_ips:
- 10.0.0.1
- 192.168.1.1
📥 Output:
allow 10.0.0.1;
allow 192.168.1.1;
4. Using Default Values
Use case: Fallback when a variable isn’t defined.
✅ Template (sshd_config):
Port {{ ssh_port | default(22) }}
📄 YAML (empty or missing ssh_port
):
# ssh_port not defined
📥 Output:
Port 22
5. String Transformation
Use case: Format or sanitize variables.
✅ Template:
user = {{ fqdn | lower | replace('.example.com', '') }}
📄 YAML:
fqdn: WEB01.EXAMPLE.COM
📥 Output:
user = web01
🛠️ Real Example 1: Deploying Custom MOTD
🎯 Goal:
- Create a login banner (motd) that shows each server’s hostname.
- Web servers use SSH port 2900; DB servers use port 2600.
✅ Jinja2 Template (motd.j2): – Prepare Jinja2 template for motd config file display banner with hostname of managed host

✅ Jinja2 Template (sshd_config.jija): – Prepare Jinja2 template for sshd configuration file with port based on value of group variabels.

📄 Group Variables: – Define variable file include sshd_port value for webserver and dbserver group

🧾 Playbook:
-Writing playbook using module template to deploy Jinja2 templates and using handler to restart service sshd after change configuration port listening.
- name: Configure motd and ssh service for all host
become: true
hosts: all
tasks:
- name: Deploy motd file for all host
template:
src: motd.jija
dest: /etc/motd
- name: Backup sshd configuration file before change port
shell: cp /etc/ssh/sshd_config /etc/ssh/sshd_config_bk2020
- name: Deploy new sshd configurtaion file for change listen port
template:
src: sshd_config.jija
dest: /etc/ssh/sshd_config
mode: 600
owner: root
group: root
notify:
- restart sshd
handlers:
- name: restart sshd
service:
name: sshd
state: restarted
Running runbook on Ansible Control node:

– Checking motd file and new ssh port listening on managed hosts


Filters in Jinja2 (Built-In Magic)
Jinja2 filters are powerful tools used to transform, reformat, or clean variable data before it’s rendered in your playbooks or templates. A filter does not modify the original variable, but instead returns a modified result on the fly.
🧠 Why Use Filters?
- You may receive data in raw form (e.g. uppercase hostnames, duplicated list values, undefined variables) and need to clean or format it.
- Filters help you avoid extra tasks or complex conditionals — simply apply them inline.
- You can use them both in template files and within Ansible playbooks (e.g.,
debug
,set_fact
, etc.).
🔧 Where Do Filters Come From?
- Some filters are built into Jinja2 language (e.g.,
default
,upper
,replace
) - Others are available via Ansible plugins (e.g.,
ipaddr
,to_yaml
,json_query
)
📚 You can find the full list of available filters and documentation here:
🔗 https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html
Real Example 3: Extracting IPs with ipaddr Filter
🎯 Goal: Using filter ipaddr to get valid IP address from a messy list
📄 Variable File (vars.yml):

🧾 Playbook:
- name: Get Valid IP Address From List Of Variables
hosts: localhost
vars_files:
- list_of_ip
tasks:
- name: Valid IP Address
debug:
msg: "{{ mylist | ipaddr }}"
📥 Output:

– Or can using filter ipaddr to get list of IP Address with subnet mask anc convert to CIDR network and prefix as below example:
📄 Variable File (vars.yml):

🧾 Playbook:
- name: Get Valid IP Address From List Of Variables
hosts: localhost
vars_files:
- list_of_ip
tasks:
- name: Valid IP Address
debug:
msg: "{{ mylist | ipaddr('network/prefix') }}"
📥 Output:

✨Summary
We’ve covered quite a bit — from understanding what Jinja2 is, to deploying custom config files like MOTD and SSHD, and even processing messy IP lists using filters. If you’ve made it this far, congrats! You’re no longer just copying nginx.conf
from StackOverflow 😄
Here’s what to remember:
✅ Jinja2 makes your config files dynamic and reusable, saving you from writing hundreds of near-identical files.
✅ Variables define the “what”; templates define the “how” — and Jinja2 makes them work together.
✅ Filters help clean, shape, or validate your data so you don’t have to manually adjust values in YAML or templates.
✅ One playbook + one template = many smart, customized deployments. That’s the real power of automation.
Whether you’re managing 3 servers or 3,000, this approach scales, simplifies, and — let’s face it — feels a bit magical
📣 You Might Also Like
If you enjoyed this guide, you may also find this related article useful:
🔗 Automate User Account Inventory Across Linux Servers Using Ansible
It walks you through automating user audits across multiple servers using Ansible — perfect for combining shell scripting and configuration management.
Your means of telling the whole thing in this piece of writing is truly fastidious, all can effortlessly understand it, Thanks a lot https://shaik.ca/blog/
Your mode of describing everything in this
piece of writing is actually pleasant, every one can without difficulty understand
it, Thanks a lot https://purlux.ca/fr/nos-services/epilation/electrolyse/
Your means of describing the whole thing in this piece of
writing is truly pleasant, all be capable of simply understand it, Thanks a lot https://fenestrationdessommets.com/produits/fenetres/
Your way of telling all in this post is in fact fastidious, every one can simply be aware of it, Thanks
a lot https://chaussuressemy.com/boutique/
Your way of telling all in this article is in fact fastidious, all be capable of without difficulty understand it, Thanks
a lot https://www.lepetitdep.com/