Example Templates
Copy-pasteable Jinja2 template examples for VLAN, ACL, BGP, OSPF, and interface configs across Cisco IOS, NX-OS, Juniper JunOS, and Arista EOS.
Overview
This page provides ready-to-use Jinja2 template examples for common network configurations. Each example includes the complete template source, a JSON object of example variables, and the rendered output so you can see exactly what the template produces when you render it.
Templates in NetStacks are rendered with minijinja (a Rust implementation of Jinja2). The examples use only core Jinja2 constructs — {% for %} loops, {% if %} conditionals, the is defined test, and the default filter — so they render identically in the preview and produce predictable output.
The examples cover multiple vendor platforms and configuration types:
| Example | Platform | Use Case |
|---|---|---|
| VLAN Configuration | Cisco IOS | Create VLANs with SVI gateways |
| Access Control List | Cisco IOS | Extended ACL with permit/deny rules |
| BGP Neighbor | Cisco NX-OS | eBGP peering with route maps |
| OSPF Area | Juniper JunOS | OSPF area with interface costs |
| Interface Configuration | Arista EOS | L3 interfaces with VRF assignment |
Copy any example into a new Jinja template, adjust the variables for your environment, and render it to produce device-ready configuration text.
How It Works
Each example follows the same three-part pattern:
- Template source — the raw Jinja2 code you paste into a new template
- Variables — a JSON object showing the expected variable names, types, and example values
- Rendered output — the configuration text produced when the template is rendered with the example variables
Examples target a specific vendor CLI syntax. When adapting an example for a different platform, you change the command syntax but keep the template logic. For example, Cisco IOS uses interface GigabitEthernet0/1 while Juniper JunOS uses set interfaces ge-0/0/0. The Jinja2 structure (loops, conditionals, variable patterns) translates across platforms unchanged.
You can build a single template that supports multiple vendors by branching on a platform variable: {% if platform == 'cisco_ios' %} ... {% elif platform == 'juniper_junos' %} ... {% endif %}. See the Jinja2 Syntax page for conditional patterns.
Step-by-Step Guide
Follow these steps to use any example from this page.
Step 1: Choose an Example
Browse the examples below and find one that matches your configuration use case. Each example targets a specific vendor platform and configuration type.
Step 2: Create a New Template
In NetStacks, create a new document with the Jinja content type and paste the example's template source into the content field. Give it a descriptive name like vlan-config-ios or bgp-neighbor-nxos. Only Jinja-type documents can be rendered.
Step 3: Adjust Variables for Your Environment
The example variable values use realistic but generic data. Replace them with values specific to your network: your VLAN IDs, your ASN, your IP addressing scheme. Keep the JSON structure (object keys, nested arrays) the same as the example expects.
Step 4: Render and Review the Output
Use the render preview to verify the output matches what you expect. Behind the UI, this calls the agent render endpoint:
POST /api/docs/{id}/render
Content-Type: application/json
{
"variables": { "vlans": [ { "id": 100, "name": "USERS" } ] }
}The response returns the rendered configuration and a success flag. On a syntax or render error, success is false and error contains the message:
{
"output": "vlan 100\n name USERS\n!",
"success": true,
"error": null
}Step 5: Use the Rendered Configuration
Copy the rendered output and apply it to your devices using your normal change process, or reuse the template as a service inside a stack. See Stack Templates for combining templates into multi-service deployments.
Code Examples
1. VLAN Configuration (Cisco IOS)
Creates VLANs and their corresponding SVI gateway interfaces on a Cisco IOS switch.
{# VLAN Configuration Template - Cisco IOS #}
{# Variables: vlans (list of objects with id, name, gateway, mask) #}
{% for vlan in vlans %}
vlan {{ vlan.id }}
name {{ vlan.name }}
!
{% endfor %}
{% for vlan in vlans %}
interface Vlan{{ vlan.id }}
description {{ vlan.name }} Gateway
ip address {{ vlan.gateway }} {{ vlan.mask }}
{% if vlan.dhcp_helper is defined %}
ip helper-address {{ vlan.dhcp_helper }}
{% endif %}
no shutdown
!
{% endfor %}{
"vlans": [
{ "id": 100, "name": "USERS", "gateway": "10.1.100.1", "mask": "255.255.255.0", "dhcp_helper": "10.0.0.50" },
{ "id": 200, "name": "VOICE", "gateway": "10.1.200.1", "mask": "255.255.255.0", "dhcp_helper": "10.0.0.50" },
{ "id": 300, "name": "PRINTERS", "gateway": "10.1.30.1", "mask": "255.255.255.0" },
{ "id": 999, "name": "NATIVE", "gateway": "10.1.99.1", "mask": "255.255.255.0" }
]
}vlan 100
name USERS
!
vlan 200
name VOICE
!
vlan 300
name PRINTERS
!
vlan 999
name NATIVE
!
interface Vlan100
description USERS Gateway
ip address 10.1.100.1 255.255.255.0
ip helper-address 10.0.0.50
no shutdown
!
interface Vlan200
description VOICE Gateway
ip address 10.1.200.1 255.255.255.0
ip helper-address 10.0.0.50
no shutdown
!
interface Vlan300
description PRINTERS Gateway
ip address 10.1.30.1 255.255.255.0
no shutdown
!
interface Vlan999
description NATIVE Gateway
ip address 10.1.99.1 255.255.255.0
no shutdown
!2. Access Control List (Cisco IOS)
Creates an extended ACL with permit and deny rules from a structured list.
{# Extended ACL Template - Cisco IOS #}
{# Variables: acl_name, rules (list), enable_logging, apply_interface, acl_direction #}
ip access-list extended {{ acl_name }}
{% for rule in rules %}
{{ rule.action }} {{ rule.protocol }} {{ rule.source }} {{ rule.source_wildcard }} {{ rule.destination }} {{ rule.destination_wildcard }}{% if rule.port is defined %} eq {{ rule.port }}{% endif %}{% if enable_logging | default(false) %} log{% endif %}
{% endfor %}
deny ip any any{% if enable_logging | default(false) %} log{% endif %}
!
interface {{ apply_interface }}
ip access-group {{ acl_name }} {{ acl_direction | default('in') }}{
"acl_name": "ACL-SERVERS-INBOUND",
"rules": [
{ "action": "permit", "protocol": "tcp", "source": "10.1.100.0", "source_wildcard": "0.0.0.255", "destination": "10.1.50.0", "destination_wildcard": "0.0.0.255", "port": "443" },
{ "action": "permit", "protocol": "tcp", "source": "10.1.100.0", "source_wildcard": "0.0.0.255", "destination": "10.1.50.0", "destination_wildcard": "0.0.0.255", "port": "22" },
{ "action": "permit", "protocol": "icmp", "source": "10.1.0.0", "source_wildcard": "0.0.255.255", "destination": "10.1.50.0", "destination_wildcard": "0.0.0.255" }
],
"enable_logging": true,
"apply_interface": "GigabitEthernet0/1",
"acl_direction": "in"
}ip access-list extended ACL-SERVERS-INBOUND
permit tcp 10.1.100.0 0.0.0.255 10.1.50.0 0.0.0.255 eq 443 log
permit tcp 10.1.100.0 0.0.0.255 10.1.50.0 0.0.0.255 eq 22 log
permit icmp 10.1.0.0 0.0.255.255 10.1.50.0 0.0.0.255 log
deny ip any any log
!
interface GigabitEthernet0/1
ip access-group ACL-SERVERS-INBOUND in3. BGP Neighbor Configuration (Cisco NX-OS)
Configures BGP neighbors with address families and route maps on Cisco NX-OS.
{# BGP Configuration Template - Cisco NX-OS #}
{# Variables: bgp_asn, router_id, neighbors (list), networks (list) #}
feature bgp
!
router bgp {{ bgp_asn }}
router-id {{ router_id }}
log-neighbor-changes
{% for neighbor in neighbors %}
neighbor {{ neighbor.ip }} remote-as {{ neighbor.remote_asn }}
description {{ neighbor.description | default('BGP Peer') }}
address-family ipv4 unicast
{% if neighbor.route_map_in is defined %}
route-map {{ neighbor.route_map_in }} in
{% endif %}
{% if neighbor.route_map_out is defined %}
route-map {{ neighbor.route_map_out }} out
{% endif %}
{% if neighbor.max_prefix is defined %}
maximum-prefix {{ neighbor.max_prefix }} 80 restart 5
{% endif %}
{% endfor %}
address-family ipv4 unicast
{% for network in networks %}
network {{ network.prefix }}/{{ network.mask_length }}
{% endfor %}{
"bgp_asn": "65001",
"router_id": "10.255.0.1",
"neighbors": [
{
"ip": "10.0.0.2",
"remote_asn": "65002",
"description": "Transit - Provider-A",
"route_map_in": "RM-PROVIDER-A-IN",
"route_map_out": "RM-PROVIDER-A-OUT",
"max_prefix": 10000
},
{
"ip": "10.0.0.6",
"remote_asn": "65003",
"description": "Peering - IX-East",
"route_map_in": "RM-IX-EAST-IN",
"max_prefix": 5000
}
],
"networks": [
{ "prefix": "10.1.0.0", "mask_length": 16 },
{ "prefix": "10.2.0.0", "mask_length": 16 }
]
}feature bgp
!
router bgp 65001
router-id 10.255.0.1
log-neighbor-changes
neighbor 10.0.0.2 remote-as 65002
description Transit - Provider-A
address-family ipv4 unicast
route-map RM-PROVIDER-A-IN in
route-map RM-PROVIDER-A-OUT out
maximum-prefix 10000 80 restart 5
neighbor 10.0.0.6 remote-as 65003
description Peering - IX-East
address-family ipv4 unicast
route-map RM-IX-EAST-IN in
maximum-prefix 5000 80 restart 5
address-family ipv4 unicast
network 10.1.0.0/16
network 10.2.0.0/164. OSPF Area Configuration (Juniper JunOS)
Configures an OSPF area with interface assignments, costs, and passive flags using Juniper JunOS "set" syntax. Note the parenthesized not (intf.passive | default(false)) test — the parentheses make the precedence explicit so the negation applies to the defaulted value.
{# OSPF Area Configuration - Juniper JunOS #}
{# Variables: ospf_area, router_id, area_type, bfd_enabled, interfaces (list) #}
set routing-options router-id {{ router_id }}
set protocols ospf area {{ ospf_area }} area-type {{ area_type | default('normal') }}
{% for intf in interfaces %}
set protocols ospf area {{ ospf_area }} interface {{ intf.name }} interface-type {{ intf.type | default('p2p') }}
{% if intf.cost is defined %}
set protocols ospf area {{ ospf_area }} interface {{ intf.name }} metric {{ intf.cost }}
{% endif %}
{% if intf.passive | default(false) %}
set protocols ospf area {{ ospf_area }} interface {{ intf.name }} passive
{% endif %}
{% if intf.authentication_key is defined %}
set protocols ospf area {{ ospf_area }} interface {{ intf.name }} authentication md5 1 key {{ intf.authentication_key }}
{% endif %}
{% endfor %}
{% if bfd_enabled | default(false) %}
{% for intf in interfaces %}
{% if not (intf.passive | default(false)) %}
set protocols ospf area {{ ospf_area }} interface {{ intf.name }} bfd-liveness-detection minimum-interval 300
{% endif %}
{% endfor %}
{% endif %}{
"ospf_area": "0.0.0.0",
"router_id": "10.255.0.1",
"area_type": "normal",
"bfd_enabled": true,
"interfaces": [
{ "name": "xe-0/0/0.0", "type": "p2p", "cost": 10, "authentication_key": "0spfK3y!" },
{ "name": "xe-0/0/1.0", "type": "p2p", "cost": 100 },
{ "name": "lo0.0", "type": "p2p", "passive": true }
]
}set routing-options router-id 10.255.0.1
set protocols ospf area 0.0.0.0 area-type normal
set protocols ospf area 0.0.0.0 interface xe-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface xe-0/0/0.0 metric 10
set protocols ospf area 0.0.0.0 interface xe-0/0/0.0 authentication md5 1 key 0spfK3y!
set protocols ospf area 0.0.0.0 interface xe-0/0/1.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface xe-0/0/1.0 metric 100
set protocols ospf area 0.0.0.0 interface lo0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface lo0.0 passive
set protocols ospf area 0.0.0.0 interface xe-0/0/0.0 bfd-liveness-detection minimum-interval 300
set protocols ospf area 0.0.0.0 interface xe-0/0/1.0 bfd-liveness-detection minimum-interval 300lo0.0 sets passive: true, so not (intf.passive | default(false)) is false and the BFD line is skipped. The two xe interfaces have no passive key, so default(false) yields false, the negation is true, and BFD is applied. The filter binds tighter than not, but the parentheses make that explicit and avoid any ambiguity when adapting the template.
5. Interface Configuration (Arista EOS)
Configures Layer 3 interfaces with IP addressing, VRF assignment, and optional VRRP on Arista EOS switches.
{# Interface Configuration Template - Arista EOS #}
{# Variables: interfaces (list with name, ip, prefix_length, description, vrf, mtu, vrrp_enabled, vrrp_vip, vrrp_priority, vrrp_group) #}
{% for intf in interfaces %}
interface {{ intf.name }}
description {{ intf.description }}
{% if intf.vrf is defined %}
vrf {{ intf.vrf }}
{% endif %}
ip address {{ intf.ip }}/{{ intf.prefix_length }}
{% if intf.mtu is defined %}
mtu {{ intf.mtu }}
{% endif %}
{% if intf.vrrp_enabled | default(false) %}
vrrp {{ intf.vrrp_group | default(1) }} ipv4 {{ intf.vrrp_vip }}
vrrp {{ intf.vrrp_group | default(1) }} priority {{ intf.vrrp_priority | default(100) }}
{% endif %}
no shutdown
!
{% endfor %}{
"interfaces": [
{
"name": "Ethernet1/1",
"description": "Uplink to Spine-01",
"ip": "10.0.1.1",
"prefix_length": 31,
"mtu": 9214
},
{
"name": "Ethernet1/2",
"description": "Uplink to Spine-02",
"ip": "10.0.1.3",
"prefix_length": 31,
"mtu": 9214
},
{
"name": "Vlan100",
"description": "Users Gateway",
"vrf": "PROD",
"ip": "10.1.100.2",
"prefix_length": 24,
"vrrp_enabled": true,
"vrrp_vip": "10.1.100.1",
"vrrp_priority": 110
},
{
"name": "Vlan200",
"description": "Voice Gateway",
"vrf": "PROD",
"ip": "10.1.200.2",
"prefix_length": 24,
"vrrp_enabled": true,
"vrrp_vip": "10.1.200.1",
"vrrp_priority": 100
}
]
}interface Ethernet1/1
description Uplink to Spine-01
ip address 10.0.1.1/31
mtu 9214
no shutdown
!
interface Ethernet1/2
description Uplink to Spine-02
ip address 10.0.1.3/31
mtu 9214
no shutdown
!
interface Vlan100
description Users Gateway
vrf PROD
ip address 10.1.100.2/24
vrrp 1 ipv4 10.1.100.1
vrrp 1 priority 110
no shutdown
!
interface Vlan200
description Voice Gateway
vrf PROD
ip address 10.1.200.2/24
vrrp 1 ipv4 10.1.200.1
vrrp 1 priority 100
no shutdown
!Questions & Answers
- Q: Can I modify these examples for my environment?
- A: Yes. These examples are starting points. Copy the template source into a new Jinja document, then modify the variable names, default values, conditional logic, and command syntax to match your network standards. The template structure and Jinja2 patterns are the same regardless of your specific values.
- Q: Do these examples work across different vendor platforms?
- A: Each example is written for a specific platform (Cisco IOS, NX-OS, Juniper JunOS, or Arista EOS) because CLI syntax differs between vendors. The Jinja2 template logic (loops, conditionals, filters) works the same everywhere — you only need to change the configuration commands themselves.
- Q: How do I handle vendor-specific syntax differences?
- A: You have two options: create separate templates per vendor, or create a single multi-vendor template that branches on a
platformvariable using{% if platform == 'cisco_ios' %}conditionals. Separate templates are simpler to maintain; multi-vendor templates reduce duplication. - Q: Which Jinja filters and tests do these examples rely on?
- A: Only core minijinja built-ins: the
defaultfilter, theis definedtest,{% for %}loops, and{% if %}/{% elif %}conditionals. No custom filters are registered by the renderer, so anything documented for standard Jinja2/minijinja works the same here. See the Jinja2 Syntax page for the full list. - Q: Can I combine multiple examples into a single stack?
- A: Yes. A stack template can reference multiple templates as services. For the exact ordering and service model, see Stack Templates — the stack-specific behavior is documented there, not on this page.
- Q: How do I preview the rendered output?
- A: Use the render preview in the template editor. It sends
POST /api/docs/{id}/renderwith a JSON body of{ "variables": { ... } }and returns{ output, success, error }. Only documents with theJinjacontent type can be rendered; rendering any other type returns a validation error. See Rendering & Preview. - Q: Are these examples tested against real devices?
- A: The examples use standard CLI syntax for each vendor platform and produce valid configuration output. However, your specific device software version may have minor syntax differences. Always use the render preview to verify the output before applying it to production devices.
Troubleshooting
Template produces wrong syntax for my device
Verify that the template matches your device platform and software version. Cisco IOS, IOS-XE, NX-OS, and IOS-XR all use different CLI syntax. The examples specify the target platform in the template comment header. If your device uses a different syntax, modify the template commands accordingly.
Variable name mismatches when adapting an example
If you rename variables in the template, make sure to update all references. For example, if you rename vlans to vlan_list, update both the {% for %} loop and the variable values you provide. Compare the extracted variable list against your JSON keys to verify all names match.
Loop or property errors with nested data structures
When looping over objects with nested properties (like {{ vlan.id }}), make sure your variable values use the exact property names the template expects. A mismatch — for example ID in your JSON versus id in the template — resolves to an undefined value rather than the data you intended.
If a template produces unexpected output, render it with a minimal set of variables first. Add complexity incrementally — start with one loop iteration, verify the output, then add more items.
Render returns success: false with an error message
The render endpoint returns success: false and a message in error when the template fails to parse or render — for example an unbalanced {% if %}/{% endif %}, a typo in a tag, or referencing an undefined value in a way that raises an error. Read the message, fix the template or variables, and re-render.
Rendered output has extra whitespace or blank lines
Jinja2 preserves whitespace around control tags by default. Use whitespace control dashes ({%- -%}) on loop and conditional tags to strip extra blank lines from the output. See the Jinja2 Syntax page for whitespace control details.
Related Features
Continue building your template library with these related resources:
- Template Basics — Create and manage templates in NetStacks
- Jinja2 Syntax — Reference for filters, conditionals, loops, and advanced patterns
- Variables & Extraction — How to structure variable values and use auto-population sources
- Rendering & Preview — Preview rendered output and validate syntax before use
- Stack Templates — Combine templates into multi-service deployment stacks