Manage multiple systemd service instances#
With systemd you can manage services like in post Environment variables set by systemd, but managing multiple instances of the same service is not so easy. For example, you have a service that runs a web server, and you want to run multiple instances of that service, each with different configuration. You can do that by creating multiple service files, but that is not very elegant. You can also use systemd templating, but that is not very flexible. The best way to do this is to use systemd instances.
The example service#
For the example, we will create a service that runs a Python script. The script will print the value of an environment variable. The name of the environment variable will be the name of the instance. The script will be called env_variables.py
and will be located in /usr/local/bin/
. The service will be called env_variables@.service
and will be located in /etc/systemd/system/
. So let’s copy the script from the previous post as it can print the value of an environment variable to the standard output.
/usr/local/bin/env_variables.py
##!/usr/bin/env python3
import os
if os.environ.get('VAR1'):
var1 = os.environ['VAR1']
else:
var1 = 'default'
print(var1)
The file needs to be executable and relies on the env command to find a version of Python 3.
Creating the template service#
With systemd templating, you can create a template service file that can be used to create multiple instances of the same service. The template service file is called env_variables@.service
and is located in /etc/systemd/system/
. The @
in the name of the file is important as it tells systemd that this is a template service file. The template service file looks like this:
/etc/systemd/system/env_variables@.service
#[Unit]
Description=App for %i
[Service]
ExecStart=/usr/local/bin/env_variables.py
Environment="VAR1=$i"
[Install]
WantedBy=multi-user.target
Note
The %i
or %I
in the Description
and Environment
lines is the instance name. From systemd documentation, the only difference should be about escaping:
%i
| Instance name | For instantiated units: this is the string between the “@” character and the suffix of the unit name.%I
| Unescaped instance name | Same as “%i”, but with escaping undone. This will resolve escape sequences like “x20” to their literal character value.
After creating the template service file, you need to reload the systemd daemon to make it aware of the new service file.
$ sudo systemctl daemon-reload
Starting an instances#
Now that the service exists, you can start an instance of it. To do that, you need to use the @
in the name of the service. The name of the instance will be the string after the @
. For example, to start an instance called customer01
, you need to run:
$ sudo systemctl enable --now app@customer01
Created symlink /etc/systemd/system/multi-user.target.wants/[email protected] → /etc/systemd/system/[email protected].
$ sudo systemctl status app@customer01
○ [email protected] - App for customer01
Loaded: loaded (/etc/systemd/system/[email protected]; enabled; vendor preset: enabled)
Active: inactive (dead) since Mon 2023-07-17 20:23:23 UTC; 6min ago
Main PID: 3493888 (code=exited, status=0/SUCCESS)
CPU: 101ms
Jul 17 20:23:23 ubuntu systemd[1]: Started App for customer01.
Jul 17 20:23:23 ubuntu env_variables.py[3493888]: customer01
Jul 17 20:23:23 ubuntu systemd[1]: [email protected]: Deactivated successfully.
If another instance is needed, you can create it and start it the same way as shown below where we create an second instance called customer02
. It also shows the status of the instance.
$ sudo systemctl enable --now app@customer02
Created symlink /etc/systemd/system/multi-user.target.wants/[email protected] → /etc/systemd/system/[email protected].
$ sudo systemctl status app@customer02
○ [email protected] - App for customer02
Loaded: loaded (/etc/systemd/system/[email protected]; enabled; vendor preset: enabled)
Active: inactive (dead) since Mon 2023-07-17 20:23:28 UTC; 7min ago
Process: 3493933 ExecStart=/usr/local/bin/env_variables.py (code=exited, status=0/SUCCESS)
Main PID: 3493933 (code=exited, status=0/SUCCESS)
CPU: 106ms
Jul 17 20:23:27 ubuntu systemd[1]: Started App for customer02.
Jul 17 20:23:28 ubuntu env_variables.py[3493933]: customer02
Jul 17 20:23:28 ubuntu systemd[1]: [email protected]: Deactivated successfully.
When checking the status of the instances, you can see that they are inactive. This is because the service is not a daemon. It is a one-shot service that runs the script and then exits. The instances are not running anymore. To see the output of the script, you can use the journalctl command.
$ sudo journal -f
Jul 17 20:23:23 ubuntu systemd[1]: Started App for customer01.
Jul 17 20:23:23 ubuntu env_variables.py[3493888]: customer01
Jul 17 20:23:23 ubuntu systemd[1]: [email protected]: Deactivated successfully.
Jul 17 20:23:26 ubuntu systemd[1]: Reloading.
Jul 17 20:23:27 ubuntu systemd[1]: Started App for customer02.
Jul 17 20:23:28 ubuntu systemd[1]: Starting Daily apt download activities...
Jul 17 20:23:28 ubuntu env_variables.py[3493933]: customer02
Jul 17 20:23:28 ubuntu systemd[1]: [email protected]: Deactivated successfully.
Final thoughts about systemd instances#
With systemd instances, you can create multiple instances of the same service. This is useful when you need to run the same service multiple times with different parameters. It is also useful when you need to run the same service multiple times with the same parameters. Examples are different instances of an application server like Apache Tomcat, PHP, or a Django application, but the options are endless.