User Services With Systemd
Your very own systemd
Recently, I’ve started using systemd
order to manage personal services on my servers.
Before that, I had set up classic system-wide unit files or, for more ad-hoc services, started the process inside of a screen
session, then detaching, hoping the process didn’t crash overnight.
Using a user instance of systemd
is way better, since you get stuff like journalctl
to view logs and RestartAlways
keep things running after random crashes. Plus, all configuration now lives in $HOME
making it easy to edit and back up.
In this post, I will show you how to set this up on any modern Debian-based system (or any distro that uses systemd
, really).
Debian 12.6 (bookworm) was used at the time of writing this post.
Caveats
There are a few things to note with this approach:
-
User units cannot depend on system-wide units, or vice versa. For example, your user service cannot depend on system-wide
redis-server
. To specify dependencies between your services, you’ll need to run all of those dependencies as user services. -
User units are run without privileges so naturally they can’t do
root
things like edit files outside of$HOME
and listen below port1024
. -
Environment variables set in
~/.zshrc
,~/.bashrc
, etc are not inherited. See the environment variables section for more information on how to set/import environment variables for user units.
Getting Started
Enable linger for your user
By default, systemd
user instances are only started after the first login of that user then killed once their last session is closed. This behavior makes sense in a desktop environment but not so much for a server environment where you typically want your daemons running at all times.
In order to change this behavior, you have to enable linger for your user by running the following:
$ loginctl enable-linger
You can verify if lingering is enabled by ensuring the column for LINGER
says yes
for your user when running:
$ loginctl list-users
Create configuration directory
The unit files for our services will live in $HOME/.config/systemd/user/
. Ensure it’s created using by using mkdir -p
, like so:
$ mkdir -p $HOME/.config/systemd/user/
Setting up an example service
We can now place this example.service
unit in $HOME/.config/systemd/user/
:
[Unit]
Description=Example Service
[Service]
Restart=always
RestartSec=1s
WorkingDirectory=/home/me/my-service-dir
ExecStart=racket hello-world-server.rkt
[Install]
WantedBy=default.target
Reload unit files
Just like system-wide systemd
, if you edit or create new unit files for your user, you’ll need to reload your copy of systemd
by running systemctl daemon-reload
, like so:
$ systemctl --user daemon-reload
Start service on boot
This starts example
as soon as your copy systemd
is launched (which should be at boot, since we enabled lingering):
$ systemctl --user enable example
Starting, Stopping and Status
You can now run the same systemctl
commands you are used to, except they are run as your user (instead of root
) and now require the --user
argument:
$ systemctl --user start example
$ systemctl --user stop example
$ systemctl --user status example
$ systemctl --user restart example
Logs
Similar to systemctl
, you’ll need to pass --user
to journalctl
:
$ journalctl --user --unit=example
More information
You can read more about this feature on the Arch Linux Wiki: systemd/User.