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.