Running a User Service with Systemd on Fedora 33
Why Run a User Service under systemd
I have a small project on Github, netmon, that I have been using to learn about writing golang web applications. The Go web application uses a Python-based program called speedtest-cli to roughly measure network connection speeds using speedtest.net’s APIs and services.
I have been running netmon on a few older Raspberry Pi computers,
a Raspberry Pi 1 Model
B+ and a
Raspberry Pi 2 Model
B. The RPi 1 has
been running Raspbian (now Raspberry Pi
OS) and the RPi 2 has
been running Ubuntu MATE (16.04). netmon would run on these
machines for days, weeks, months and I was lazy, until recently, and would just
start up the program by hand, put the software in the background, and let it
run. Eventually, I decided to run the service using
systemd so that it would run automatically every time the
system restarted. I created the systemd service files and, for convenience,
ran the software from my home directory where I could modify it easily. Ubuntu
let me run the software from my home directory despite being a system-level
systemd service. I should note netmon was run as unprivileged user since it
didn’t need special permissions (mainly just network access).
In the last few weeks, the Ubuntu MATE machine started having issues, restarting
regularly. So, I rebuilt it using Fedora Server 33 for 32-bit
Arm. I tried using the systemd service files
that I had created before for Ubuntu, but they didn’t work because
SELinux was preventing the software from running.
This makes sense because the SELinux setup under Fedora (and other Red
Hat-related distributions) are setup so that system service executables can’t
run from a user’s home directory–only specific system directories.
While I could have just installed netmon in a system directory, I preferred
having the binary in my home directory to make it easier for me to update and
modify. As noted above, it didn’t really need to run as a privileged user, so I
decided to figure out how to run the program as a user-level systemd service.
Of course, the catch was that, while I have done system-level services with
systemd, I hadn’t done any as a user-level service.
Setting Up the Service
I found a few useful pages on the topic, including a page on the Arch
Wiki. As noted on the Arch
Wiki page, a user can put their own systemd units (i.e., service files) in
~/.config/systemd/user/. Once the unit file has been created, you can run the
service using systemctl --user enable myservice, where myservice is the name
of the service installed in ~/.config/systemd/user/.
So for netmon, I took the system-level systemd unit file and modified it
slightly, removing the User declaration in the [Service] section of the file
since the software will be running as me since it is a user-level service. I
then placed it in ~/.config/systemd/user and ran systemctl --user enable netmon and systemctl --user start netmon to try to start the service.
The problem I ran into next is that the PATH environment variable in my
~/.bashrc was not being used by the service, so it couldn’t find
speedtest-cli. In the Environment Variable section of the Arch Wiki
page on user-level systemd, it suggests creating a .conf file with the
NAME=VAL pairs defining the needed environment.
In my case, I created a file called netmon.conf file in
~/.config/systemd/user with the needed PATH defined (e.g.,
PATH=/home/myname/bin).
Finally, systemd user services don’t continue past a user’s login session.
The Automatic start-up of systemd user
instances
section on the same Arch Wiki page describes how to make the user service run
without the user logged in and at system startup time. It comes down to running
the following command for the specific user:
loginctl enable-linger username
With all of that in place, I was able to get the user-level service running much
like a system-level service using systemd.