systemd/User
systemd offers the ability to manage services under the user's control with a per-user systemd instance, enabling them to start, stop, enable, and disable their own user units. This is convenient for daemons and other services that are commonly run for a single user, such as mpd, or to perform automated tasks like fetching mail.
How it works
As per default configuration in /etc/pam.d/system-login
, the pam_systemd
module automatically launches a systemd --user
instance when the user logs in for the first time. This process will survive as long as there is some session for that user, and will be killed as soon as the last session for the user is closed. When #Automatic start-up of systemd user instances is enabled, the instance is started on boot and will not be killed. The systemd user instance is responsible for managing user services, which can be used to run daemons or automated tasks with all the benefits of systemd, such as socket activation, timers, dependency system, and strict process control via cgroups.
Similar to system units, user units are located in the following directories (ordered by ascending precedence):
/usr/lib/systemd/user/
where units provided by installed packages belong.~/.local/share/systemd/user/
where units of packages that have been installed in the home directory belong./etc/systemd/user/
where system-wide user units are placed by the system administrator.~/.config/systemd/user/
where the user puts their own units.
When a systemd user instance starts, it brings up the per user target default.target
. Other units can be controlled manually with systemctl --user
. See systemd.special(7) § UNITS MANAGED BY THE USER SERVICE MANAGER.
- Be aware that the
systemd --user
instance is a per-user process, and not per-session. The rationale is that most resources handled by user services, like sockets or state files will be per-user (live on the user's home directory) and not per session. This means that all user services run outside of a session. As a consequence, programs that need to be run inside a session will probably break in user services. The way systemd handles user sessions is pretty much in flux. See [1] and [2] for some hints on where things are going. systemd --user
runs as a separate process from thesystemd --system
process. User units can not reference or depend on system units or units of other users.
Basic setup
All the user units will be placed in ~/.config/systemd/user/
. If you want to start units on first login, execute systemctl --user enable unit
for any unit you want to be autostarted.
systemctl --global enable unit
as root. Similarly for disable
.Environment variables
Units started by user instance of systemd do not inherit any of the environment variables set in places like .bashrc
etc. There are several ways to set environment variables for them:
- For users with a
$HOME
directory, create a .conf file in the~/.config/environment.d/
directory with lines of the formNAME=VAL
. Affects only that user's user unit. See environment.d(5) for more information. - Use the
DefaultEnvironment
option in/etc/systemd/user.conf
file. Affects all user units. - Add a drop-in configuration file in
/etc/systemd/system/user@UID.service.d/
, see #Service example - Add a drop-in configuration file in
/etc/systemd/system/user@.service.d/
(affects all users), see #Service example - At any time, use
systemctl --user set-environment
orsystemctl --user import-environment
. Affects all user units started after setting the environment variables, but not the units that were already running. - Using the
dbus-update-activation-environment --systemd --all
command provided by dbus. Has the same effect assystemctl --user import-environment
, but also affects the D-Bus session. You can add this to the end of your shell initialization file. - For "global" environment variables for the user environment you can use the
environment.d
directories which are parsed by some generators. See environment.d(5) and systemd.generator(7) for more information. - You can also write a systemd.environment-generator(7) script which can produce environment variables that vary from user to user, this is probably the best way if you need per-user environments (this is the case for
XDG_RUNTIME_DIR
,DBUS_SESSION_BUS_ADDRESS
, etc).
One variable you may want to set is PATH
.
After configuration, the command systemctl --user show-environment
can be used to verify that the values are correct. You may need to run systemctl --user daemon-reload
for changes to take effect immediately.
Systemd user instance
The above only addresses default environment variables for user units. However, the systemd user instance itself is also affected by some environment variables. In particular, certain specifiers (see systemd.unit(5) § SPECIFIERS) are affected by XDG variables.
However, the systemd user instance will only use environment variables that are set when it is started. In particular, it will not try parsing files, see upstream bug #29414 (closed WONTFIX). Therefore, if such environment variables are needed, they should be set in a drop-in configuration file, see #Service example.
Systemd does not provide introspection tools to check these values, however, something like the following service can be used to help checking that the specifiers expand as expected:
$XDG_CONFIG_HOME/systemd/user/test-specifiers.service
[Service] Type=oneshot ExecStart=printf '(systemd)=(envvar)\n' ExecStart=printf '%%s=%%s\n' %C "${XDG_CACHE_HOME}" ExecStart=printf '%%s=%%s\n' %E "${XDG_CONFIG_HOME}" ExecStart=printf '%%s=%%s\n' %L "${XDG_STATE_HOME}"/log ExecStart=printf '%%s=%%s\n' %S "${XDG_STATE_HOME}" ExecStart=printf '%%s=%%s\n' %t "${XDG_RUNTIME_DIR}"
Service example
Create the drop-in directory /etc/systemd/system/user@.service.d/
and inside create a file that has the extension .conf (e.g. local.conf
):
/etc/systemd/system/user@.service.d/local.conf
[Service] Environment="PATH=/usr/lib/ccache/bin:/usr/local/sbin:/usr/local/bin:/usr/bin" Environment="EDITOR=nano -c" Environment="BROWSER=firefox" Environment="NO_AT_BRIDGE=1" Environment="XDG_STATE_HOME=%h/.local/var/state"
DISPLAY and XAUTHORITY
DISPLAY
is used by any X application to know which display to use and XAUTHORITY
to provide a path to the user's .Xauthority
file and thus the cookie needed to access the X server. If you plan on launching X applications from systemd units, these variables need to be set. Systemd provides a script in /etc/X11/xinit/xinitrc.d/50-systemd-user.sh
to import those variables into the systemd user session on X launch. [3] So unless you start X in a nonstandard way, user services should be aware of the DISPLAY
and XAUTHORITY
.
PATH
If you customize your PATH
and plan on launching applications that make use of it from systemd units, you should make sure the modified PATH
is set on the systemd environment. Assuming you set your PATH
in .bash_profile
, the best way to make systemd aware of your modified PATH
is by adding the following to .bash_profile
after the PATH
variable is set:
~/.bash_profile
systemctl --user import-environment PATH
- This will not affect systemd services started before
PATH
is imported. - systemd does not look at the set
PATH
when resolving non-absolute binaries itself.
pam_env
Environment variables can be made available through use of the pam_env.so
module. See Environment variables#Using pam_env for configuration details.
Automatic start-up of systemd user instances
The systemd user instance is started after the first login of a user and killed after the last session of the user is closed. Sometimes it may be useful to start it right after boot, and keep the systemd user instance running after the last session closes, for instance to have some user process running without any open session. Lingering is used to that effect. Use the following command to enable lingering for your own user, if polkit is installed:
$ loginctl enable-linger
Without polkit or to enable lingering for a different user:
# loginctl enable-linger username
To list all users which have the permit for lingering see column "LINGER" with yes
:
$ loginctl list-users
or inspect /var/lib/systemd/linger
. To revoke lingering:
# loginctl disable-linger username
Writing user units
See systemd#Writing unit files for general information about writing systemd unit files.
Example
The following is an example of a user version of the mpd service:
~/.config/systemd/user/mpd.service
[Unit] Description=Music Player Daemon [Service] ExecStart=/usr/bin/mpd --no-daemon [Install] WantedBy=default.target
Example with variables
The following is a user service used by foldingathomeAUR, which takes into account variable home directories where Folding@home can find certain files:
~/.config/systemd/user/foldingathome-user.service
[Unit] Description=Folding@home distributed computing client After=network.target [Service] Type=simple WorkingDirectory=%h/.config/fah ExecStart=/usr/bin/FAHClient CPUSchedulingPolicy=idle IOSchedulingClass=3 [Install] WantedBy=default.target
As detailed in systemd.unit(5) § SPECIFIERS, the %h
variable is replaced by the home directory of the user running the service. There are other variables that can be taken into account in the systemd manpages.
Reading the journal
The journal for the user can be read using the analogous command:
$ journalctl --user
To specify a unit, one can use
$ journalctl --user-unit myunit.service
Or, equivalently:
$ journalctl --user -u myunit.service
Temporary files
systemd-tmpfiles allows users to manage custom volatile and temporary files and directories just like in the system-wide way (see systemd#systemd-tmpfiles - temporary files). User-specific configuration files are read from ~/.config/user-tmpfiles.d/
and ~/.local/share/user-tmpfiles.d/
, in that order. For this functionality to be used, it is needed to enable the necessary systemd user units for your user:
$ systemctl --user enable systemd-tmpfiles-setup.service systemd-tmpfiles-clean.timer
The syntax of the configuration files is the same than those used system-wide. See the systemd-tmpfiles(8) and tmpfiles.d(5) man pages for details.
Xorg and systemd
There are several ways to run xorg within systemd units. Below there are two options, either by starting a new user session with an xorg process, or by launching xorg from a systemd user service.
Automatic login into Xorg without display manager
This option will launch a system unit that will start a user session with an xorg server and then run the usual ~/.xinitrc
to launch the window manager, etc. You need to have xlogin-gitAUR installed. Set up your xinitrc as specified in the Xinit#xinitrc section.
The session will use its own dbus daemon, but various systemd utilities will automatically connect to the dbus.service
instance. Finally, enable the xlogin@username
service for automatic login at boot. The user session lives entirely inside a systemd scope and everything in the user session should work just fine.
Xorg as a systemd user service
Alternatively, xorg can be run from within a systemd user service. This is nice since other X-related units can be made to depend on xorg, etc, but on the other hand, it has some drawbacks explained below.
xorg-server provides integration with systemd in two ways:
- Can be run unprivileged, delegating device management to logind (see Hans de Goede commits around this commit).
- Can be made into a socket activated service (see this commit).
Unfortunately, to be able to run xorg in unprivileged mode, it needs to run inside a session. So, right now the handicap of running xorg as user service is that it must be run with root privileges (like before 1.16), and cannot take advantage of the unprivileged mode introduced in 1.16.
GetSessionByPID
using its own pid as argument. See this thread and xorg sources. It seems likely that xorg could be modified to get the session from the tty it is attaching to, and then it could run unprivileged from a user service outside a session.This is how to launch xorg from a user service:
1. Make xorg run with root privileges for any user, by editing /etc/X11/Xwrapper.config
. This builds on Xorg#Xorg as Root by adding the stipulation that this need not be done from a physical console. That is, allowed_user
's default of console
is being overwritten with anybody
; see Xorg.wrap(1).
/etc/X11/Xwrapper.config
allowed_users=anybody needs_root_rights=yes
2. Add the following units to ~/.config/systemd/user
~/.config/systemd/user/xorg@.socket
[Unit] Description=Socket for xorg at display %i [Socket] ListenStream=/tmp/.X11-unix/X%i
~/.config/systemd/user/xorg@.service
[Unit] Description=Xorg server at display %i Requires=xorg@%i.socket After=xorg@%i.socket [Service] Type=simple SuccessExitStatus=0 1 ExecStart=/usr/bin/Xorg :%i -nolisten tcp -noreset -verbose 2 "vt${XDG_VTNR}"
where ${XDG_VTNR}
is the virtual terminal where xorg will be launched, either hard-coded in the service unit, or set in the systemd environment with
$ systemctl --user set-environment XDG_VTNR=1
3. Make sure to configure the DISPLAY
environment variable as explained above.
4. Then, to enable socket activation for xorg on display 0 and tty 2 one would do:
$ systemctl --user set-environment XDG_VTNR=2 # So that xorg@.service knows which vt use $ systemctl --user start xorg@0.socket # Start listening on the socket for display 0
Now running any X application will launch xorg on virtual terminal 2 automatically.
The environment variable XDG_VTNR
can be set in the systemd environment from .bash_profile
, and then one could start any X application, including a window manager, as a systemd unit that depends on xorg@0.socket
.
Some use cases
Window manager
To run a window manager as a systemd service, you first need to run #Xorg as a systemd user service. In the following we will use awesome as an example:
~/.config/systemd/user/awesome.service
[Unit] Description=Awesome window manager After=xorg.target Requires=xorg.target [Service] ExecStart=/usr/bin/awesome Restart=always RestartSec=10 [Install] WantedBy=wm.target
[Install]
section includes a WantedBy
part. When using systemctl --user enable
it will link this as ~/.config/systemd/user/wm.target.wants/window_manager.service
, allowing it to be started at login. Is recommended to enable this service, not to link it manually.Persistent terminal multiplexer
Rather than logging you into a window manager session for your user session by default, you may want to automatically run a terminal multiplexer (such as screen or tmux) in the background.
Create the following:
~/.config/systemd/user/multiplexer.target
[Unit] Description=Terminal multiplexer Documentation=info:screen man:screen(1) man:tmux(1) After=cruft.target Wants=cruft.target [Install] Alias=default.target
Separating login from X login is most likely only useful for those who boot to a TTY instead of to a display manager (in which case you can simply bundle everything you start in mystuff.target
).
The dependency cruft.target
, like the mystuff.target
above, allows starting anything which should run before the multiplexer starts (or which you want started at boot regardless of timing), such as a GnuPG daemon session.
You then need to create a service for your multiplexer session. Here is a sample service, using tmux as an example and sourcing a gpg-agent session which wrote its information to /tmp/gpg-agent-info
. This sample session, when you start X, will also be able to run X programs, since $DISPLAY
is set:
~/.config/systemd/user/tmux.service
[Unit] Description=tmux: A terminal multiplexer Documentation=man:tmux(1) After=gpg-agent.service Wants=gpg-agent.service [Service] Type=forking ExecStart=/usr/bin/tmux start ExecStop=/usr/bin/tmux kill-server Environment=DISPLAY=:0 EnvironmentFile=/tmp/gpg-agent-info [Install] WantedBy=multiplexer.target
Enable tmux.service
, multiplexer.target
and any services you created to be run by cruft.target
, start user@.service
as usual and you should be done.
Kill user processes on logout
Arch Linux builds the systemd package with --without-kill-user-processes
, setting KillUserProcesses
to no
by default. This setting causes user processes not to be killed when the user logs out. To change this behavior in order to have all user processes killed on the user's logout, set KillUserProcesses=yes
in /etc/systemd/logind.conf
.
Note that changing this setting breaks terminal multiplexers such as tmux and GNU Screen. If you change this setting, you can still use a terminal multiplexer by using systemd-run
as follows:
$ systemd-run --scope --user command args
For example, to run screen
you would do:
$ systemd-run --scope --user screen -S foo
Using systemd-run
will keep the process running after logout only while the user is logged in at least once somewhere else in the system and user@.service
is still running.
After the user logs out of all sessions, user@.service
will be terminated too, by default, unless the user has "lingering" enabled [8]. To effectively allow users to run long-term tasks even if they are completely logged out, lingering must be enabled for them. See #Automatic start-up of systemd user instances and loginctl(1) for details.
Troubleshooting
Runtime directory '/run/user/1000' is not owned by UID 1000, as it should
systemd[1867]: pam_systemd(systemd-user:session): Runtime directory '/run/user/1000' is not owned by UID 1000, as it should. systemd[1867]: Trying to run as user instance, but $XDG_RUNTIME_DIR is not set
If you see errors such as this and your login session is broken, it is possible that another system (non-user) service on your system is creating this directory. This can happen for example if you use a docker container that has a bind mount to /run/user/1000
. To fix this, you can either fix the container by removing the mount, or disable/delay the docker service.