Recently the need occurred for us to run API services from user accounts rather than elevated access (i.e. root). I have since come to like this rather a lot as systemd makes this super easy and in the long run allows more self-management on regular user accounts that need to run daemon services. This is fairly ideal for unprivileged micro services run on shared servers. The basic idea is that every user can run their own systemd services and therefore every user can operate a daemon (if allowed to).
Setting this up initially has some pitfalls though, so I thought I would write down how this is best made to work.
First things first. To make use of this you need systemd, logind and journald. Additionally you’ll need pam_systemd and it needs to be loaded for sessions (distributions will usually set this up automatically for you, if not have fun editing /etc/pam.d/ ;)).
We will also need the actual systemd service/unit file. Generally, everything is the same as if you were to write a regular system service. Ultimately this also means that you can use the same service file for system-wide use or user-limited use so long as the actual service doesn’t require elevated permissions for anything.
A simple example could be this:
[Unit] Description=Statifier [Service] Environment=PORT=9000 ExecStart=/home/statifier/bin/statifier Restart=always [Install] WantedBy=default.target
Of note is the install target which will enable our service to be started by the default target (i.e. this service would get auto-started on boot).
Before we can get started some additional settings are needed
- Enable lingering for the user. This allows user services to exist outside active logind sessions, consequently this needs to be done for any new user which should be able to do this.
loginctl enable-linger $USERNAME
- Enable persistent journald logging. This is optional but without it users are not able to read their own logs unless in the systemd-journal system group.
mkdir /var/log/journal && systemctl restart systemd-journald
- Re-log on the lingering user to make sure permissions are properly applied etc.
To install the service file you’ll want to place it in the home-directory-bound XDG directory as described in the systemd.unit manpage. Usually this would be
Once you placed your .service file in there you’ll probably need to reload the daemon to get it to reload the file
systemctl --user daemon-reload
Once everything is configured and installed we can get rocking by running the commands as the user itself.
Start the service with
systemctl --user start statifier.service
Verify it started properly with
systemctl --user status statifier.service
Enable the service for autostart via target with
systemctl --user enable statifier.service
Look at the logs with
journalctl --user statifier.service
By putting everything together you can deploy new code or changes to the service file via sftp and reload and restart the service via ssh systemctl. Allowing for really simple deployment code and no sysadmin involvement beyond the initial setup. And thanks to journald you don’t have to worry about logging since it will gobble up all output and know it came from this service.
I for one love it!