1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
+++
title = "User Services With Systemd"
date = "2024-08-18T19:05:10-04:00"
tags = ["systemd","selfhosting"]
+++
# 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 port `1024`.
* Environment variables set in `~/.zshrc`, `~/.bashrc`, etc are not inherited. See the [environment variables section](https://wiki.archlinux.org/title/Systemd/User#Environment_variables) 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](https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html) 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](https://wiki.archlinux.org/title/Systemd/User).
|