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:
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 email@example.com 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.
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 firstname.lastname@example.org $ sudo systemctl start email@example.com
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.
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.)
Great series on systemd!
How about another one on SELinux?
Chuck, glad you’re enjoying the series! Perhaps we will be doing one on SELinux. Will keep your suggestion in mind!
Regarding the OpenVPN unit, it could use quite a few improvements. See also: https://github.com/OpenVPN/openvpn/pull/22
Interesting point. Sounds like getting in touch with OpenVPN developers on their mailing lists or IRC would be most effective 🙂