Using Templates


For config file generation, we provide a backend service which can bind config.xml data to templates written in Jinja2 (

All available templates should be installed at the following location on the OPNsense system:

  1. /usr/local/opnsense/service/templates/

Naming convention

All templates should be put into a directory structure containing the vendor and package/application name, our sample application is placed inside the directory:


Template package content

Every template directory should contain at least 3 files:

  1. a manifest file, containing meta information about the package, named +MANIFEST
  2. a content descriptor, containing the actual targets, named +TARGETS
  3. one or more template(s)


A manifest should contain the following information, for documentation purposes:

name: <the name of the package>
version: < the version number>
origin: <original install location, for example opnsense/sample>
comment: <package comments>
desc: <full description>
maintainer: <email adres of the maintainer>
www: <website address>


The +TARGETS file contains the source template name inside the template directory and the (dynamic) target filename divided by a colon (:) multiple lines may be inserted per file.

For example :


Will create a file /tmp/template_sample/simple_page.txt using the template example_simple_page.txt.

If you want to use information from within the config.xml file as output filename, you can use tags to address the content, like [version] to input the tag version from the xml file. When generating multiple files from 1 template, you can use one wildcard (%) to address a section of the config file, for example [interfaces.%.if] loops over the interfaces and outputs the value of if.


For more information of the template language itself, please look at and the examples installed in /usr/local/opnsense/service/templates/OPNsense/Sample.

There’s one special case when using the template engine, every wildcard used for the output file generation is also provided to the template, so you are able to determine which filter let to this output.

Those filters are stored in the variable TARGET_FILTERS.

{% if TARGET_FILTERS['interfaces.wan'] %}
{% endif %}

Test usage

The templates can be rendered via the backend service (configd), to test this functionality on a running OPNsense system, use:

configctl template reload OPNsense.Sample

Python template usage example

The template system itself is a separate module which is used by configd, to use (or test) the system without the daemon, use:

# import template system and config.xml handling
from modules import template
from modules import config

# construct a new template object, set root to /tmp/
tmpl = template.Template(target_root_directory="/tmp/")
# open the config.xml and bind to template object
conf = config.Config('/config.xml')

# generate output for OPNsense/Sample
generated_filenames = tmpl.generate('OPNsense.Sample')

# print results
for filename in generated_filenames:
  print ('.. generated : %s'%filename)