For AI agents: a documentation index is available at the root level at /llms.txt and /llms-full.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
    • NVIDIA Switch Infrastructure
    • I want to...
  • Quick Start
    • Start Here
    • Getting Started with Config Manager
    • TUI Wizard Reference
    • Configuration Samples
    • Interfaces
    • Local Development Quick Start
    • First Run Tour
  • Config Manager Overview
    • Config Manager Concepts
    • Getting Started with Nautobot
  • User Guides
    • New Site Bringup
    • Workflow Lifecycle
  • Deployment
    • Hosting Options
    • Network Topology Requirements
    • Firewall Ports
    • Airgapped Deployment
    • Troubleshooting
  • Services
      • Network Template Rendering System
      • Render Service
      • Filter quick reference
      • Template expansion
NVIDIANVIDIA
Developer-friendly docs for your API
Privacy Policy | Your Privacy Choices | Terms of Service | Accessibility | Corporate Policies | Product Security | Contact

Copyright © 2026, NVIDIA Corporation.

LogoLogo
On this page
  • Entrypoint Selection
  • Built-In Expansion Example
  • Plugin Discovery
  • Plugin Template Layout
  • Plugin Data and Filters
  • Local Plugin Testing
  • Deployment
ServicesTemplate Rendering Service

Template and Plugin Expansion Walkthrough

||View as Markdown|
Previous

Jinja2 filter quick reference

Next

Healthcheck

This walkthrough shows how the render engine selects entrypoint templates, expands Jinja inheritance, and loads external template plugins. It is intended for external users who need to build their own template plugins while reusing the Config Manager template engine.

Entrypoint Selection

For each device, the renderer loads device data and location data from Nautobot, then selects every entrypoint below this logical path:

<normalized-platform>/<normalized-role>/<intended-firmware>/entrypoint/

The platform and role values come from Nautobot and are normalized by lower-casing and replacing whitespace with hyphens. The firmware value comes from the device config context key intended-firmware.version.

For a device with platform Cumulus Linux, role Storage Leaf, and intended firmware 5.16.1, the renderer looks under:

cumulus-linux/storage-leaf/5.16.1/entrypoint/

Each matching .j2 file renders one output file. The render service stores the output by stripping the path and .j2 suffix, so entrypoint/startup.yaml.j2 becomes startup.yaml.

Built-In Expansion Example

A Cumulus storage leaf startup entrypoint starts as a thin version-specific composition file:

1{% extends "cumulus-linux/storage-leaf/base/startup.yaml.j2" %}
2
3{% block service %}
4{% include "cumulus-linux/role_common/5.16.1/include/service.j2" %}
5{% endblock service %}
6
7{% block qos %}
8{% include "cumulus-linux/storage-leaf/5.16.1/include/qos.j2" %}
9{% endblock qos %}
10
11{% block router %}
12{% include "cumulus-linux/storage-leaf/5.16.1/include/router.j2" %}
13{% endblock router %}
14
15{% block interfaces %}
16{% include "cumulus-linux/storage-leaf/5.16.1/include/interface.j2" %}
17{% endblock interfaces %}
18
19{% block system %}
20{% include "cumulus-linux/role_common/5.16.1/include/system.j2" %}
21{% endblock system %}

The role base inherits from broader common behavior:

1{% extends "cumulus-linux/superpod-common/base/startup.yaml.j2" %}
2
3{% block acl %}
4{% include "cumulus-linux/storage-leaf/include/acl.j2" %}
5{% endblock acl %}
6
7{% block bridge %}
8{% include "cumulus-linux/storage-leaf/include/bridge.j2" %}
9{% endblock bridge %}
10
11{% block interfaces %}
12{% include "cumulus-linux/storage-leaf/include/interface.j2" %}
13{% endblock interfaces %}
14
15{% block router %}
16{% include "cumulus-linux/storage-leaf/include/router.j2" %}
17{% endblock router %}
18
19{% block vrf %}
20{% include "cumulus-linux/storage-leaf/include/vrf.j2" %}
21{% endblock vrf %}

The common base defines the output skeleton and required blocks:

1{% block header %}
2- set:
3{% endblock header %}
4
5{% block acl %}{% endblock acl %}
6{% block bridge %}{% endblock bridge %}
7{% block evpn %}{% endblock evpn %}
8{% block interfaces required %}{% endblock interfaces %}
9{% block nve %}{% endblock nve %}
10{% block qos %}{% endblock qos %}
11{% block router required %}{% endblock router %}
12
13{% block service %}
14{% include "cumulus-linux/superpod-common/include/service.j2" %}
15{% endblock service %}
16
17{% block system %}
18{% include "cumulus-linux/superpod-common/include/system.j2" %}
19{% endblock system %}
20
21{% block vrf required %}{% endblock vrf %}

The final render is the merged result of those layers:

  1. The version-specific entrypoint selects the firmware-specific overrides.
  2. The role base supplies role-level blocks and includes.
  3. The common base supplies the file skeleton and default blocks.
  4. Jinja replaces each include with the included template output.
  5. Filters convert Nautobot and plugin data into the values used by the templates.

Plugin Discovery

Template plugins are normal Python packages. A plugin registers an entry point in the nv_config_manager_templates.plugins group:

1[project]
2name = "example-config-manager-templates"
3version = "0.1.0"
4dependencies = ["nv-config-manager-templates"]
5
6[project.entry-points."nv_config_manager_templates.plugins"]
7example = "example_config_manager_templates"

The plugin module can provide any combination of templates, filters, GraphQL queries, or location resolution:

1from pathlib import Path
2from typing import Any
3
4from example_config_manager_templates.filters import get_custom_filters as _filters
5
6
7def get_template_paths() -> list[Path]:
8 return [Path(__file__).parent / "templates"]
9
10
11def get_custom_filters() -> dict[str, Any]:
12 return _filters()
13
14
15def get_graphql_queries() -> dict[str, str]:
16 return {}
17
18
19def get_location_name(device_data: dict[str, Any]) -> str | None:
20 return None

At renderer startup, Config Manager:

  1. Discovers installed entry points from nv_config_manager_templates.plugins.
  2. Calls plugin hooks such as get_template_paths(), get_custom_filters(), and get_graphql_queries().
  3. Adds plugin template paths before the built-in package templates.
  4. Loads built-in filters.
  5. Loads plugin filters that do not conflict with an existing filter name.
  6. Executes plugin GraphQL queries during data loading and passes results as plugin_data.

Because plugin template paths are loaded before the built-in templates, a plugin can add new logical paths or intentionally shadow a built-in template path. Shadowing should be explicit and covered by render tests. Prefer extends and small block overrides when the plugin only needs to adjust part of a built-in template.

Plugin Template Layout

A plugin template root uses the same logical layout as the built-in templates:

src/example_config_manager_templates/templates/
cumulus-linux/
example-storage-leaf/
base/
startup.yaml.j2
include/
qos.j2
5.16.1/
entrypoint/
startup.yaml.j2
include/
qos.j2

For a new Nautobot device role, create a role path that matches the normalized role name. For example, role Example Storage Leaf resolves to example-storage-leaf.

An entrypoint can reuse a built-in base template and override only the blocks that differ:

1{% extends "cumulus-linux/storage-leaf/base/startup.yaml.j2" %}
2
3{% block qos %}
4{% include "cumulus-linux/example-storage-leaf/5.16.1/include/qos.j2" %}
5{% endblock qos %}

If the plugin needs shared behavior for multiple plugin-owned roles, use plugin-owned common role names instead of broad names that look built-in:

cumulus-linux/example-common/base/startup.yaml.j2
cumulus-linux/example-storage-leaf/base/startup.yaml.j2
cumulus-linux/example-border-leaf/base/startup.yaml.j2

Plugin Data and Filters

The render context always includes:

device_data
location_data

When plugin queries are registered, the context can also include:

plugin_data

Plugin query results are keyed by query name. Templates should not traverse raw plugin GraphQL response paths directly. Instead, expose stable domain concepts through plugin filters:

1def fabric_policy(plugin_data: dict, policy_name: str) -> dict:
2 query_data = plugin_data.get("fabric_policy") or {}
3 ...

Then call that filter from the template:

1{% set policy = plugin_data|fabric_policy("default") %}

This keeps Nautobot plugin model changes isolated to Python filters instead of spreading response-shape assumptions across templates.

Local Plugin Testing

Install the plugin into the same Python environment as nv-config-manager-templates, then use template-cli for local iteration.

Cache core and plugin query data:

$uv run template-cli cache-query \
> --hostname sw01 \
> --nautobot-url https://nautobot.example.com \
> --token-file ~/.nautobot-token \
> --output-file tests/resources/nautobot/sw01.json \
> --output-location-file tests/resources/nautobot/DC01.json \
> --output-plugin-file tests/resources/nautobot/sw01-plugin-data.json

Render from cached data:

$uv run template-cli render \
> --cached-data tests/resources/nautobot/sw01.json \
> --cached-location-data tests/resources/nautobot/DC01.json \
> --cached-plugin-data tests/resources/nautobot/sw01-plugin-data.json \
> --entrypoint startup.yaml.j2

Without --vault, template-cli render disables Vault lookups and returns placeholder secret values. Use this mode for render tests, not for production device configuration.

For regression coverage, add cached Nautobot JSON, cached plugin data when needed, and expected rendered output for every plugin role and entrypoint.

Deployment

External deployments load template plugins through the installer or Helm chart, not through template-cli.

With the installer, add plugin directories or plugin tarballs on the Template Plugins screen. The installer stages that content into the render service template plugin PVC and restarts render workloads only when the staged template content changes.

For Helm or GitOps-managed deployments, set renderService.templatePlugins.enabled and provide plugin source images when needed. A plugin source image can provide:

/plugin-wheels/*.whl
/plugin-source/pyproject.toml

The render service installs those plugin wheels or source packages into its runtime environment so their nv_config_manager_templates.plugins entry points are discoverable.

Rendered configuration metadata records both the engine package version and installed plugin package versions:

engine=nv-config-manager-templates:<engine-version>;plugins=<plugin>:<plugin-version>

That version vector lets Config Manager compare rendered configurations against the exact template engine and plugin set that produced them.