a Glance at Systemd

It’s been a long while that Unix used init.d to manage services.

But it comes with two shortages: the one thing is that what it only does is run a simple script, no more, no less. So, the script itself needs to handle all the other stuff, like executing some command after another service is started, etc. The other thing is, init.d execute all the scripts sequentially, it makes the machine start slow.

Therefore, Lennart Poettering wrote Systemd to be a replacement. To be clear, not everyone thinks init.d didn’t do its job well, because it’s separate from the operating system while Systemd doesn’t, so it’s violating “Unix philosophy“.

Systemd components graphic

As Systemd is a big topic to focus on, this article will only cover the most common usages of Systemd. For more information, refer to wikis like ArchWiki, or if you prefer an old-class, textbook like document, try this.


Basic Acknowledgments

Daily Commands

systemctl start nginx   # Start a Servce
systemctl stop nginx    # Stop a Service
systemctl restart nginx # Restart a Service

Unit

Unit is a file format how Systemd recognize a “startup task” (service in fact).

Unit files can be stored in multiple places, and you can check it on your own system by running this command.

systemctl show --property=UnitPath

In most cases. it will be stored in /etc/systemd/system/ for system provided units, and /usr/lib/systemd/system/ for units provided by installed packages. But if you want a “task” to start automatically at boot, it must be linked to /etc/systemd/system/.

Enable & disable

As we mentioned before, Systemd is built to simplify the boot process. So, the symbolic link to /etc/systemd/system/ would be created by enable & disable command. The two following are doing the identical thing.

systemctl enable unitname.service
ln -s '/usr/lib/systemd/system/unitname.service''/etc/systemd/system/multi-user.target.wants/unitname.service'

Target

Did you see the “ulti-user.target.wants” in the last command? That’s the second to last thing you must know about Systemd, target.

A system will have significant amounts of units to be started up in a single boot. So, grouping some units together and starting them at once will be better than specifying which unit should be started separately.

So, if you want your application to run automatically when the system boots into command line (which is destined to happen Lol), just add your unit into “group” multi-user.target. If your application needs graphical interface to function, then add your unit into “group” graphical.target.

To demonstrate which target is started by default, run

systemctl get-default

Blocks

The unit file is made up with serval blocks. All the information like the name of the unit, what to do when start/restart/stop the unit, and so on.

[Unit]

The starting block usually is [Unit]. It defines what this unit is and the relationship with other units. The most used fields are listed below, while full references are available here.

DescriptionDo what you think it Dose.
DocumentationA space-separated list of URIs referencing documentation for this unit or its configuration.
WantsConfigures (weak) requirement dependencies on other units. If the listed units fail to start or cannot be added to the transaction, this has no impact on the validity of the transaction, and this unit will still be started.
RequiresLike Wants but declares a stronger requirement dependency.
BindsToConfigures requirement dependencies, similar in style to Requires. However, this dependency type is stronger: in addition to the effect of Requires, it declares that if the unit bound to is stopped, this unit will be stopped too.
Before/AfterThose two settings configure ordering dependencies between units. If unit foo.service contains the setting Before=bar.service and both units are being started, bar.service‘s start-up is delayed until foo.service has finished starting up. After is the inverse of Before.
ConflictsConfigures negative requirement dependencies. If a unit has a Conflicts setting on another unit, starting the former will stop the latter and vice versa.

[Service]

The following block could be [Service]. It defends the most behavior of our unit. The most used fields are listed below, while full references are available here.

ExecStartCommands with their arguments that are executed when this service is started.
ExecReloadCommands to execute to trigger a configuration reload in the service.
ExecStopCommands to execute to stop the service started via ExecStart.
RestartConfigures whether the service shall be restarted when the service process exits, is killed, or a timeout is reached.
Takes one of noon-successon-failureon-abnormalon-watchdogon-abort, or always. If set to no (the default), the service will not be restarted.

In addition, I would recommend using “on-failure” for field Restart for most common situations, because in most cases, errors that occur with programs that are set to start automatically are transient. Restarting them helps.

Also in most cases, ExecStart is the only field you need to care about, since stop and restart commands will handle all the processes that started by ExecStart.

[Install]

This is the last block in a unit file. It is used to define which target this unit is to start with.

WantedByA symbolic link is created in the .wants/ or .requires/ directory of each of the listed units when this unit is installed by systemctl enable.

WantedBy usually takes one of graphical.target, multi-user.target. Note that graphical.target depends on multi-user.target.


Example

This example is going to show a simple service that executes a executable file when machine starts.

First step of course, creating a unit file. Since it is just a plain text file, nano will be decent.

cd /usr/lib/systemd/system/
sudo nano unitname.service

Then write the unit file.

[Unit]
Description=Some HTTP server
After=remote-fs.target sqldb.service memcached.service
Requires=sqldb.service memcached.service

[Service]
ExecStart=/usr/sbin/some-fancy-httpd-server

[Install]
WantedBy=multi-user.target

Save the file.

Then reload the Systemd daemon list.

systemctl daemon-reload

Last, enable the unit.

systemctl enable unitname

Leave a Reply