Render inventory with templates
Templates use Jinja expressions to read data from the inventory and render them.
Use Templates → Data reference in the UI to see the supported datatypes, fields, filters, and relationships available in the version you are running. There are also examples there, as this changes between versions .
A small template workflow
- Create a configuration template and choose an output filename such as
nginx.conf,traefik.yml, orinventory-report.md. - Start with Render now to preview the template against current accepted inventory.
- Generate an output snapshot. A snapshot records the template revision and rendered content.
- Inspect History & outputs before approving or publishing anything.
Saving source creates a new revision. Keep generated files tied to the revision that produced them; this makes a changed route or missing device explainable later.

The template editor displays source and current rendered output.
Nginx reverse-proxy configuration
This example emits one HTTP server block for each gateway route. It follows each route to its service, chooses the first application for that service, then finds an address for that application. Routes without a usable application or address are skipped rather than rendered as invalid upstreams.
{% for route in data.gateway_routes.sort('fqdn') %}
{% set service = route.service %}
{% set application = data.applications.of(service).first() %}
{% set address = data.ip_addresses.of(application).first() if application else none %}
{% if application and address %}
upstream {{ route.fqdn|replace('.', '_') }} {
server {{ address.address }}:{{ application.port }};
}
server {
listen 80;
server_name {{ route.fqdn }};
location {{ route.path_prefix or '/' }} {
proxy_pass http://{{ route.fqdn|replace('.', '_') }};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
{% endif %}
{% endfor %}
Point your deployment at the generated file, then validate it with the target tool (nginx -t) before
reloading Nginx. A template render is not a deployment and does not validate Nginx syntax for you.
Traefik dynamic configuration
Traefik's file provider accepts YAML. This example emits one route for Grafana; change the FQDN filter and
service name for another route. The optional path prefix becomes a PathPrefix matcher.
{% set route = data.gateway_routes.filter('fqdn:eq:grafana.home.example').first() %}
{% set application = data.applications.of(route.service).first() if route else none %}
{% set address = data.ip_addresses.of(application).first() if application else none %}
{% if route and application and address %}
http:
routers:
grafana:
rule: "Host(`{{ route.fqdn }}`){% if route.path_prefix %} && PathPrefix(`{{ route.path_prefix }}`){% endif %}"
service: grafana
services:
grafana:
loadBalancer:
servers:
- url: "http://{{ address.address }}:{{ application.port }}"
{% endif %}
Save this as a YAML output, run it through your normal YAML and Traefik checks, and only then copy or publish it to Traefik's watched directory. If a service has several applications, make the selection rule explicit, using a tag or sorted/limited query, instead of relying on the first record.
A human-readable inventory report
Templates can also produce human-readable reports. This example renders a Markdown inventory report.
# {{ data.site.name }} inventory report
Generated: {{ data.generated_at }}
Networks: {{ data.networks.count() }}
Devices: {{ data.devices.count() }}
Services: {{ data.services.count() }}
## Devices
{% for device in data.devices.sort('name') %}
### {{ device.name }}
{% if device.hostname %}- Hostname: {{ device.hostname }}
{% endif %}- Type: {{ device.device_type.name if device.device_type else 'Unclassified' }}
{% if device.description %}- Notes: {{ device.description }}
{% endif %}{% for address in data.ip_addresses.of(device) %}- Address: {{ address.address }}
{% endfor %}
{% endfor %}
## Services
{% for service in data.services.sort('name') %}
- **{{ service.name }}**{% if service.category %} ({{ service.category }}){% endif %}
{% endfor %}
Keep reports bounded for large sites: filter to a site-scoped collection, call limit() when appropriate, and
reuse a query stored with {% set ... %}. MiniPAM limits data operations, materialized records, and output
size so a template cannot run indefinitely.
Snapshots, checks, and automation
Use the separate asset-integrity checks to assert that important inventory or output assumptions still hold. Attach blocking checks to a configuration template before enabling automatic publication.
Automation can run on a schedule or after the fixed inventory-change coalescing window. Start with manual previews and snapshots, validate the generated file with its target application, then enable one trigger. Rendering stays read-only; publication is the explicit final step.
