Post

Linux Services (Part 2)

In this post we shall talk more about config files for services and also write a few simple services of our own as a practice!

Service File

A service file is divided into three primary sections - Unit, Service and Install. It is important that we don’t change the syntax of a service file, it is case sensitive!

Let’s understand more what each section does.

Unit Secion

DirectiveDescriptionExampleNotes
Description=A brief explanation of what the service does.Description=Hello World ServiceThis text is visible in systemctl status.
After=Specifies dependencies for starting the service. The service starts only after these units.After=network.targetEnsures that the specified units are started before this service.
Requires=Lists units that are essential for the service. If a required unit fails, the service stops.Requires=basic.targetImplies a hard dependency between the service and the listed units.
Wants=Specifies units that should start alongside the service but are not mandatory.Wants=network-online.targetA softer dependency; failure of these units won’t stop the service.
Conflicts=Specifies units that should not run simultaneously with this service.Conflicts=foo.serviceStops the conflicting service if it’s running.
Before=Specifies that this unit must start before the listed units.Before=multi-user.targetUsed to control the startup order.
OnFailure=Lists units to trigger if this service fails.OnFailure=alert-email.serviceTypically used to send alerts or take corrective actions.

Service Secion

DirectiveDescriptionExampleNotes
ExecStart=The command that starts the service.ExecStart=/usr/bin/python3 /usr/local/bin/hello_service.pyRuns the specified command or script to start the service.
Restart=Determines the behavior when the service exits or fails.Restart=alwaysOptions: always, on-failure, no. always ensures the service is restarted in all cases.
User= and Group=Specifies the user and group under which the service runs.User=nobody Group=nogroupRunning as a non-root user improves security.
WorkingDirectory=Sets the directory from which the service starts.WorkingDirectory=/var/wwwHelps ensure the service runs from a specific location.
Environment=Sets environment variables for the service.Environment="MY_ENV_VAR=value"Can set multiple variables for configuration or context.

Install Secion

DirectiveDescriptionExampleNotes
WantedBy=Specifies the target under which the service will be started or enabled.WantedBy=multi-user.targetEnsures the service starts in a specific target like multi-user.target or graphical.target.
Alias=Specifies alternative names for the service.Alias=my-service-alias.serviceAllows the service to be referenced using different names.

The Basics

Before going into writing our own service, let’s have a look at some basic commands for Linux services.

ActionCommand
Start a servicesudo systemctl start <service_name>
Stop a servicesudo systemctl stop <service_name>
Restart a servicesudo systemctl restart <service_name>
Check the status of a servicesystemctl status <service_name>
Enable a service to start at bootsudo systemctl enable <service_name>
Disable a service from starting at bootsudo systemctl disable <service_name>
Reload a service, some service provides this as a way to read updated config filessudo systemctl reload <service_name>

Example Service (SSH)

A practical service to have on your system would be SSH. It is a secure shell that allows you to access remotely to another system in with a text interface. If you haven’t have OpenSSH installed on your Ubuntu, you could easily install it by running the command below:

1
sudo apt install ssh

The config file for ssh is kept in /etc/ssh/sshd_config file.

1
sudoedit /etc/ssh/sshd_config

This is where you would want to update your ssh config, for example, by default ssh uses port 22, which you can change it to another port. Of course, only if you have the actual reason to do so.

Once something is updated in the config file, you will need to restart the ssh server for the changes to take effect.

1
sudo systemctl restart sshd

Great! Now you know how to configure a service! Let’s try to write your own service file.

Example of Your Own Service (ROOT)

Well, hold up! Before we can get there, we first need to address the difference between the service files in the etc directory and ~/.config.

DirectoryScopePurposeAccess ControlPrecedence
/etc/systemd/systemSystem-wideContains system services that can be enabled/disabled and started/stopped by administrators for all users.Requires root privileges for modifications.Overrides /usr/lib/systemd/system files.
/etc/systemd/userUser service (global)Contains user services that apply to all users on the system.Requires root privileges for modifications.Overrides /usr/lib/systemd/user files.
/lib/systemd/systemSystem-wide (default)Contains default service files provided by the OS or software packages.Typically read-only; requires root privileges for modifications.Lowest precedence; overridden by /etc/systemd/system.
~/.config/systemd/userUser service (personal)Contains user-defined services specific to a single user, useful for managing personal applications and tasks.Can be modified by the owning user.Overrides both /usr/lib/systemd/user and /etc/systemd/user.

Alrighty! Now that we know the difference, let’s do a hello.service! We will need to create the file first in /etc/systemd/system.

1
sudoedit /etc/systemd/system/hello.service

Now, add these lines into the file and save it! We are using sudoedit here because the files in /etc/systemd/system require root privileges.

1
2
3
4
5
6
7
8
9
10
11
[Unit]
Description=Hello World Service
After=network.target

[Service]
ExecStart=/usr/bin/python3 /usr/local/bin/hello_service.py
# StandardOutput=append:/var/log/hello_service.log
# StandardError=append:/var/log/hello_service_error.log

[Install]
WantedBy=multi-user.target

This service file will start run a python script that will say “Hello, World!” every 5 seconds. But since it is a daemon, we won’t be seeing the printout and will just have to believe that it is doing the thing.

For the python script, let do:

1
sudoedit /usr/local/bin/hello_service.py

Now populate the script with the following content:

1
2
3
4
5
6
7
8
9
#!/usr/bin/env python3

# This is just a dummy service
# Echo out "Hello, World!" every 5 seconds
import time

while True:
    print("Hello, World!")
    time.sleep(5)

Once you are done, we will need to tell systemd to read in changes with:

1
sudo systemctl daemon-reload

To run the service that we have just created:

1
sudo systemctl start hello

To check the status of our service

1
sudo systemctl status hello

To stop our service

1
sudo systemctl stop hello

Finally, if you want this service to start by default when you boot up your system. You can:

1
sudo systemctl enable hello

When you enable a service, systemd will symlink your service into the wants folder for the target specified in wanted by. And you can manage it with systemctl.

Example of Your Own Service (USER)

Well, sometimes we don’t always need a system wide service, for example, you just need to start a tmux script when the user has started his or her session! I chose tmux because it is particularly challenging for beginners and also it is something very practical. Say you have a robot, and you have a tmux script that can startup the entire robot system, this would be a great way to do so.

Firstly, let’s create the required directories for systemd.

1
2
mkdir ~/.config/systemd/user
touch ~/.config/systemd/user/my_tmux.service

Now grab a template tmux script from here. And place it in the tmux_conf directory inside of you home directory. Or you could simply run the commands below:

1
2
3
mkdir ~/tmux_conf
cd ~/tmux_conf
curl -O https://raw.githubusercontent.com/BruceChanJianLe/tmux/refs/heads/master/tmux_template.bash

Now edit my_tmux.service file and populate the content:

1
2
3
4
5
6
7
8
9
10
11
12
[Unit]
Description=tmux session for user %u
After=default.target

[Service]
Type=forking
ExecStart=%h/tmux_conf/tmux_template.bash
ExecStop=/usr/local/bin/tmux kill-session -t tmux_template

[Install]
# Start when the user session starts
WantedBy=default.target

Note that you can only have one tmux service, otherwise, only the first service will work. And the consecutive ones will not work unless with some minor tweak on the service file by setting RemainAfterExit=true, but the service will not be tracked correctly after so.

For user systemctl, you will need to add a flag. Let’s have a look at reloading the service files.

1
systemctl --user daemon-reload

Once our service file is ready to use, let’s enable it and reboot our computer.

1
2
systemctl --user enable my_tmux
reboot

And voilà! If you check my_tmux service, it should be up and running!

1
systemctl --user status my_tmux

In summary, we have learned about service files, managing a common service (secure shell), look at how to write our own service file for system wide and user. Until then, keep learning!

Reference

  • https://bbs.archlinux.org/viewtopic.php?id=247292
  • https://linuxhandbook.com/create-systemd-services/
  • https://wiki.archlinux.org/title/Tmux#Autostart_with_systemd
  • https://naftuli.wtf/2017/12/28/systemd-user-environment/
This post is licensed under CC BY 4.0 by the author.

Trending Tags