Notes to self

systemd user services and systemctl --user

Most users are familiar with the system-wide systemd services managed by the systemctl command. But systemd services can be made for and entirely controlled by regular unprivileged users.

Regular services on Fedora and other systems with systemd as an init system, are usually found at /etc/systemd/system/ and managed with the root privileges. Think your packaged NGINX web server or the PostgreSQL database. We usually make our own, too, to run our long-running applications.

These services have one other thing in common. Once enabled, they start and stop with the system boot and shutdown:

$ sudo systemctl enable nginx.service

That will be an essential distinction. Let’s see the other type of systemd service: one designed to be run by unprivileged users.

If we drop a systemd service in ~/.config/systemd/user, it will be picked up by systemd as a user service. The big advantage is that the particular user now manages this service without the need for sudo.

It does require the --user flag when invoking any of the systemctl commands:

$ systemctl --user enable myuser.service
$ systemctl --user daemon-reload
$ systemctl --user start myuser.service

We can even use the global /etc/systemd/user/ location, making the service available to all users at once (/usr/lib/systemd/user/ would be typically used by an RPM package on Fedora).

But what’s the real reason for having user services? To answer that, we have to realize when the enabled service starts and stops. If we enable a user service, it starts on user login, and runs as long as there is a session open for that user. Once the last session dies, the service stops.

That makes it perfect for all kinds of desktop software, and if you open /etc/systemd/user/ you might find there a lot of GNOME, Evolution, and other desktop services.

But what if we want to run a long-running service under an unprivileged user, like a web application service that persists across restarts? For this use-case, we have a User= directive that we can use in system services:

$ cat /etc/systemd/system/sharing.service
[Unit]
Description=Admin's Sharing Server

[Service]
Type=simple
User=admin
WorkingDirectory=/home/admin/shared
ExecStart=/usr/bin/python3 -m http.server 8080
Restart=always

[Install]
WantedBy=multi-user.target

If the above sharing service runs only when the admin is logged in, it would be quite limiting. So it’s a system service with the User directive within [Service] section. But it feels wrong not giving the admin an option to cancel this service.

To this end, we have to workaround it by enabling this in a sudoers file:

START="/bin/systemctl start sharing.service"
STOP="/bin/systemctl stop sharing.service"
ENABLE="/bin/systemctl enable sharing.service"
DISABLE="/bin/systemctl disable sharing.service"

echo 'admin  ALL=(ALL) NOPASSWD: $START, $STOP, $ENABLE, $DISABLE' | tee /etc/sudoers.d/admin
chmod 0440 /etc/sudoers.d/admin

Work with me

I have some availability for contract work. I can be your fractional CTO, a Ruby on Rails engineer, or consultant. Write me at strzibny@strzibny.name.

RSS