Cached at:
08/08/26, 08:37 PM
# systemd Dynamic Users
Source: [https://ethulhu.co.uk/systemd-dynamicuser](https://ethulhu.co.uk/systemd-dynamicuser)
The Linux init system[systemd](https://en.wikipedia.org/wiki/Systemd)has its controversies, but it also has some pretty neat features, my favorite of which is*Dynamic Users*\. Lennart Poettering’s blog has a[full rundown of the ins and outs](http://0pointer.net/blog/dynamic-users-with-systemd.html), but I’ll summarize the key points below, and document a few tricks\.
contents…- [Users and groups and Name Service Switch, oh my\!](https://ethulhu.co.uk/systemd-dynamicuser#users-and-groups-and-name-service-switch-oh-my)
- [Simple services](https://ethulhu.co.uk/systemd-dynamicuser#simple-services)
- [Sandboxed Steam?](https://ethulhu.co.uk/systemd-dynamicuser#sandboxed-steam)- [Implementation](https://ethulhu.co.uk/systemd-dynamicuser#implementation)
## Users and groups and Name Service Switch, oh my\!
On Linux, when you create a user it is usually added to`/etc/passwd`, and groups it is in are added to`/etc/groups`\. However, these databases and others can actually come from multiple sources, controlled by the[Name Service Switch](https://en.wikipedia.org/wiki/Name_Service_Switch), and on modern Linux one of those sources is systemd\.
When configured to, systemd will create a user for the lifetime of a Unit to run that Unit\. The user is never added to`/etc/passwd`, but exists only at runtime, and when the Unit finishes the user goes away\. Although they are very locked down by default, this user is a full Linux user, and can be added to groups, write to disk, or be given capabilities\. In particular, systemd provides a system for*State Directories*, which will be writable by the created user, persist after the Unit finishes, and will be fixed\-up the next time the Unit runs to be writeable by the next dynamically created user, which can be used to store service data, or to simulate a home directory\.
## Simple services
A very useful result of Dynamic Users is to simplify the packaging and running of services\. Instead of having to create a user to run a service when it is installed and remove it later when the service is uninstalled, the service package only needs to install a service file as the user is created at runtime\. For example, below is the configuration for a web service of mine:
```
$ cat /lib/systemd/system/log.eth.moe
[Unit]
Description=Backend server for https://log.eth.moe
[Service]
# Create a user at runtime.
# It will have a random name.
DynamicUser=yes
# Set the user's primary group to www-data.
Group=www-data
# Create a temporary runtime directory at /run/log-eth-moe/.
RuntimeDirectory=log-eth-moe
# Create a persistent state directory at /var/lib/log-eth-moe/.
StateDirectory=log-eth-moe
ExecStart=/usr/bin/log.eth.moe -socket /run/log-eth-moe/listen.sock -dir /var/lib/log-eth-moe/
[Install]
WantedBy=multi-user.target
```
It’s also generally useful any time you want a non\-human user to run something\. For example, below is[my configuration](https://github.com/ethulhu/kodi-systemd-service/)for running the[Kodi media center](https://kodi.tv/)on a Raspberry Pi:
```
$ cat /lib/systemd/system/kodi.service
[Unit]
Description=Kodi Media Center
After=systemd-user-sessions.service network.target sound.target
[Service]
# Create a user at runtime.
DynamicUser=yes
# Call that user "kodi".
User=kodi
# Add it to useful groups for a media center.
# Note that these are all supplementary groups,
# and the dynamic user has no primary group.
SupplementaryGroups=audio
SupplementaryGroups=input
SupplementaryGroups=plugdev
SupplementaryGroups=video
# Create a persistent state directory at /var/lib/kodi.
StateDirectory=kodi
# Set the home directory of the dynamic user to /var/lib/kodi.
Environment=HOME=/var/lib/kodi
ExecStart=/usr/bin/kodi-standalone
[Install]
WantedBy=multi-user.target
```
## Sandboxed Steam?
[Steam](https://store.steampowered.com/)is great, but it also requires running other people’s code, so it’s best to sandbox it a little\. While[some people](https://blog.jessfraz.com/post/docker-containers-on-the-desktop/)might go to the lengths of[running Steam in Docker](http://blog.drwahl.me/steam-running-in-docker-lxc/), I use systemd to achieve similar ends\.
The steps of this approach are:
- Create a group to share your display with\. For myself,`eth`, I created`eth\-x11`\.
- Allow members of that group to access your display\.
- Run Steam as a member of that group, directed to use your display\.
### Implementation
After creating your group, grant that group permission to use your display, substituting`eth\-x11`for your group:
```
$ xhost +si:localgroup:eth-x11
```
This will need to be done every X11 session, and should probably be added to your`\.xinitrc`or`\.xsession`\.
Then create a script to run Steam under`systemd\-run`:
- Run the command with a TTY so that Steam’s log messages are output to your terminal for debugging\.
- Create a user at runtime\.
- Set the*primary*group of the user to be`eth\-x11`\. The`xhost`command above requires it to be the primary group\.
- Add the user to the groups`audio`,`input`, and`video`\.
- Create a`StateDirectory`called`steam`, which`systemd`will put at`/var/lib/steam/`\.
- Set the`$DISPLAY`to be the current X11 display\.
- Set the`$HOME`of the running process to`/var/lib/steam/`\.
```
$ cat /usr/local/bin/steam
#!/bin/sh
set -eux
systemd-run \
--pty \
--property=DynamicUser=yes \
--property=Group=eth-x11 \
--property=SupplementaryGroups=audio \
--property=SupplementaryGroups=input \
--property=SupplementaryGroups=video \
--property=StateDirectory=steam \
--property=Environment=DISPLAY=${DISPLAY} \
--property=Environment=HOME=/var/lib/steam \
/usr/games/steam
```
Unfortunately, to use`systemd\-run`you must either be root or authenticate yourself to systemd, but the experience can be improved with`sudoers`or setuid on the script\.