Free tech learning for everyone, from basics to pro

Network AutomationProgramming and Application SecurityIntermediate

Jinja2 templates for network configuration

In short: Jinja2 is a templating engine that merges data with a text template. Network teams use it to generate consistent configurations from YAML or a source of truth. Keep data and templates separate, use StrictUndefined to catch missing values, and review the rendered output before deploying.

Why templates?

Most configuration is the same pattern with different values: hostnames, VLANs, addresses. Copying and editing by hand is where typos and inconsistencies creep in. Jinja2 is a Python templating engine that lets you write the pattern once and fill in values from data. Ansible, Nornir and Salt all use it, so the skill transfers across tools.

Template renderingDataYAML or sourceof truthTemplateJinja2 .j2fileRenderPython +Jinja2Config textper deviceDeployNetmiko,NAPALM
Figure 1. Jinja2 merges structured data with a template to produce consistent configuration text.

Template syntax

Jinja2 syntax{{ var }}insert a value{% for %}loop over items{% if %}conditional block| filtertransform a value{# note #}template comment{% include %}reuse a snippet
Figure 2. The six pieces of Jinja2 syntax you will use in almost every network template.

Double braces insert values. Brace-percent tags hold logic such as loops and if statements. Filters, written after a pipe, transform values, for example | upper, | default("none") or | join(","). Here is an access switch template saved as templates/access.j2:

hostname {{ hostname }}
!
{% for vlan in vlans %}
vlan {{ vlan.id }}
 name {{ vlan.name | upper }}
{% endfor %}
!
{% for port in ports %}
interface {{ port.name }}
 description {{ port.description | default("user port") }}
 switchport mode access
 switchport access vlan {{ port.vlan }}
{% if port.voice_vlan is defined %}
 switchport voice vlan {{ port.voice_vlan }}
{% endif %}
 spanning-tree portfast
{% endfor %}

Rendering from Python

Install Jinja2 with pip install jinja2. Load templates from a folder, load the data from YAML, and render. Two settings matter a lot: trim_blocks and lstrip_blocks remove the blank lines that logic tags would otherwise leave, and StrictUndefined raises an error for missing variables instead of silently printing nothing.

import yaml
from jinja2 import Environment, FileSystemLoader, StrictUndefined

env = Environment(
    loader=FileSystemLoader("templates"),
    trim_blocks=True,
    lstrip_blocks=True,
    undefined=StrictUndefined,
)
template = env.get_template("access.j2")

with open("acc-sw1.yaml", encoding="utf-8") as f:
    data = yaml.safe_load(f)

config = template.render(**data)
with open("output/acc-sw1.cfg", "w", encoding="utf-8") as f:
    f.write(config)
print(config)

Matching data file acc-sw1.yaml:

hostname: acc-sw1
vlans:
  - {id: 10, name: users}
  - {id: 20, name: voice}
ports:
  - {name: GigabitEthernet1/0/1, vlan: 10, voice_vlan: 20}
  - {name: GigabitEthernet1/0/2, vlan: 10, description: printer}

Tip: render to files and review them, or diff them against the running configuration, before pushing anything. A template bug multiplies across every device.

Good practice and pitfalls

  • Keep logic light: templates should mostly insert data. Complex decisions belong in Python or in the data itself.
  • Validate data first: check VLAN ranges and addresses (for example with the ipaddress module) before rendering.
  • No secrets in data files: inject passwords and keys at render time from a vault, and never commit rendered configs containing them.
  • Autoescape is for HTML: network templates normally leave it off, but if you ever render web pages, turn autoescape on to prevent cross-site scripting.
  • Never render untrusted templates: letting users supply template text can lead to server-side template injection. Users should supply data, not templates.
SettingEffect
trim_blocks=TrueRemoves the newline after a block tag
lstrip_blocks=TrueStrips spaces before a block tag
undefined=StrictUndefinedMissing variables raise an error

Practice

Why use StrictUndefined?
Without it a misspelt or missing variable renders as an empty string, producing broken configuration silently. StrictUndefined makes rendering fail so you notice.
Where should complex decisions live: the template or Python?
Mostly in Python or in the data. Templates are easier to read and test when they mainly insert values.
What is server-side template injection and how do you avoid it?
It happens when untrusted users can supply template text that the server renders, letting them run expressions. Only render templates you control, and treat user input as data.

Key takeaways

  • Jinja2 merges data with templates to produce consistent configs.
  • Use {{ }} for values, {% %} for logic and | for filters.
  • Enable trim_blocks, lstrip_blocks and StrictUndefined.
  • Validate data, keep secrets out of files and review rendered output.
  • Never render templates supplied by untrusted users.
Tracks your learning on this device only. See your progress on the learning paths.

About the author

Mohamed Fayas

Founder of FM TecHub. CCNA certified, with a BTEC HND in Network Engineering, and BEng (Hons) in Computer Networking and Cloud Security. He writes practical, plain-English guides on networking, security and cloud.

More about the author

Original content written by Mohamed Fayas for FM TecHub. © 2026 FM TecHub. You're welcome to share a link to this page; please don't copy the text elsewhere without permission.

Stay up to date by email

New notes, tools and security news from FM TecHub, explained simply. Free.

We only use your email to send these updates. See our privacy policy.