Welcome back to our continuing series on systemd features. As you’ve seen in our previous articles, systemd brings more power and flexibility to service startup and management. Some features in systemd we’ve covered are for compatibility with the old SysVinit. However, a brand new feature in systemd is the template unit file.

What are template unit files?

Template unit files allow systemd to address multiple units from a single configuration file. You can call a systemd template unit file using a special format to use this feature:

<service_name>@<argument>.service

The argument is a bit of text (string) passed to systemd to use in the unit file. The argument can be used to customize the way systemd deals with that specific instance of the unit. Multiple instances of the same unit can exist.

Two identifiers are used in the unit file for passing the instance argument.

%i passes the argument, specially formatted (escaped)

%I passes the argument verbatim without escaping

Escaping formats the identifier with special rules before using it. Characters that would otherwise break file names are substituted.

Real world examples

OpenVPN, for example, uses template unit files to allow multiple instances using the same unit file. Here is the unit file for reference:

[Unit] Description=OpenVPN Robust And Highly Flexible Tunneling Application On %I After=network.target [Service] PrivateTmp=true Type=forking PIDFile=/var/run/openvpn/%i.pid ExecStart=/usr/sbin/openvpn --daemon --writepid /var/run/openvpn/%i.pid --cd /etc/openvpn/ --config %i.conf [Install] WantedBy=multi-user.target

Here, you can see the escaped argument %i used anywhere a filename is called. The unescaped argument %I is used in the description to be more human readable.

This setup allows you to call openvpn@myconfig.service to launch an instance of openvpn with myconfig as the configuration file name. In this case, the command would start OpenVPN with /etc/openvpn/myconfig.conf as the configuration file. It would also store a process ID file as /var/run/openvpn/myconfig.pid.

Another example is the text based terminal on every Fedora system. When the user switches consoles using Ctrl+Alt+F2, Ctrl+Alt+F3, and so on, a new terminal is spawned. In this case systemd calls a service named getty@.service. systemd searches for the unit template getty.service and provide the appropriate argument such as tty2 or tty3 to the unit file. The %i identifier provides the instance string so the terminal starts on the new console.

Example template

For example, you could write a template unit file for a fancy web server like so:

# fancy-web-server.service [Unit] Description=My HTTP server AssertPathExists=/srv/webserver [Service] Type=notify ExecStart=/usr/sbin/some-fancy-httpd-server %i.conf Nice=5 [Install] WantedBy=multi-user.target

Using the above configuration, we can launch many instances from the single template configuration file. For instance:

$ sudo systemctl start fancy-web-server@config1.service $ sudo systemctl start fancy-web-server@config2.service

This would start two instances of fancy-http-server, launching the commands

/usr/sbin/some-fancy-httpd-server config1.conf /usr/sbin/some-fancy-httpd-server config2.conf

The config1 and config2 strings are taken from the systemd command and substituted into instances of %i in the unit file.

This feature is useful since your service can work with many different configurations or settings without rewriting your service each time.

Further reading

The FreeDesktop website has additional information if you want to learn more about working with template unit files. (Hint: look for the word template in that document to see where the topic is covered.)