XDG_RUNTIME_DIR is necessary for systemctl --user to work.

I have set up ubuntu server 16.04 to run systemd user sessions. Now, when trying to administer them, I find that when changin a user via sudo -u $user -i or even su - $user, the environment does not have XDG_RUNTIME_DIR set, preventing systemctl --user from working. However, when I ssh straight into that user, it is set correctly.

If I understand the documentation correctly, this should be set by libpam-systemd when creating the user session. The user slice is started correctly, as the directory to which XDG_RUNTIME_DIR should point(/run/users/$uid) exists. I'm hesitant to just hardcode it in, say, .bash_profile, because that seems hacky (albeit working), when pam should be taking care of it.

I can, of course, add XDG_RUNTIME_DIR to env_keep in sudoers, but that would just preserve the sudoing user's environment, which is not what I want. I want the target user's environment.

What I'm really wondering, though, is how come the session is set up correctly with ssh, but not with su or sudo -i?

4 Answers 4


I have replicated this issue on my Fedora 25 system.

I found a very suspicious condition in the source code. https://github.com/systemd/systemd/blob/f97b34a/src/login/pam_systemd.c#L439 It looks as if it was written with normal sudo in mind but not sudo -u non-root-user.

machinectl shell --uid=non-root-user worked as you requested.

systemd-run did not appear to work as desired, despite the reference to it in the machinectl documentation.

Some machinectl commands don't work if you have enabled SELinux at the moment, and these specific commands didn't work for me until I did setenforce 0. However I'm in the middle of trying workarounds to get machinectl working as I want it to w.r.t SELinux, so it's possible my fiddling is what causes e.g. machinectl shell to timeout.

EDIT: I think this code was introduced after this discussion. And apparently su - / sudo -i could be made to work, but no-one has implemented it (still).

Relevant commit is https://cgit.freedesktop.org/systemd/systemd/commit/?id=baae0358f349870544884e405e82e4be7d8add9f

  • In other words, PAM will not set XDG_RUNTIME_DIR for sudo sessions by design? I guess then me setting it in ~/.profile isn't as hacky as I thought.
    – mkaito
    Mar 1, 2017 at 15:15
  • 4
    I don't want to say "by design" because I can't work out what the design is. Looking up sudo again, I see at least some builds/distributions preserve enough environment variables, that programs run as root can end up inflicting permission problems on the original user. However this isn't a reason not to set XDG_RUNTIME_DIR corresponding to the target user.
    – sourcejedi
    Mar 1, 2017 at 17:30
  • 1
    A related Q&A is unix.stackexchange.com/questions/423632 .
    – JdeBP
    Feb 13, 2018 at 8:57

What I'm really wondering, though, is how come the session is set up correctly with ssh, but not with su or sudo -i?


Sorry, but "su" is a tool for changing user identities and very few other process credentials temporarily. It's not a tool for opening a completely new login session. A new login session has a very well defined, pristine setup, not inheriting anything from any other session, but this is really not the case for "su" uid changes: most of the execution environment is inherited over, in numerous and non-obvious ways, for example MAC contexts, audit contexts, cgroup contexts, namespace contexts, scheduling, timer granularity, …

if you want a full new session, use something like "machinectl login" or "machinectl shell", which will actually get your a fully clean, independent, detach environment, with no hidden process properties leaking from where you call it into it.

logind sessions are mostly bound to the audit session concept, and audit sessions remain unaffected by "su", in fact they are defined to be "sealed off", i.e. in a way that if a process entered a session once, it will always stay with it, and so will its children, i.e. the only way to get a new session is by forking off something off PID 1 (or something similar) that never has been part of a session.

Or to say this differently: the stuff you invoke through "su" will show up just fine in "loginctl", however, it remains part of your original session, you logged in originally. You can verify that by invoking "loginctl status" on the original session's ID (which you can see through echo $XDG_SESSION_ID)

  • Note: su --login is explictly for opening a completely new login session
    – Rich
    Jul 27, 2021 at 18:54
  • 2
    In short, a workaround for sudo not setting up a normal environment for the target user is to run sudo machinectl shell [user]@.host.
    – Syncopated
    Aug 16, 2021 at 5:33

I found running sudo -E bash properly saves XDG_RUNTIME_DIR.

  • -E, --preserve-env preserve user environment when running command. Feb 8, 2023 at 5:49
  • 1
    This will wrongly copy the XDG_RUNTIME_DIR of the calling user.
    – konsolebox
    Aug 14, 2023 at 7:34

For those still wanting a sudo -i solution, you can install a pam module that creates XDG_RUNTIME_DIR. An example is pam_xdg.

Your distro is unlikely to have the pam_xdg package so to install this module you'll have to figure out how to do it yourself. I use Gentoo and I created an ebuild that installs the module, but I'm yet to publish it as my overlay has plenty of dev commits that need finalization. I'll update this answer once I uploaded it.

After properly installing the pam module, add the following line in /etc/pam.d/sudo-i:

session optional        pam_xdg.so

Specify required instead of optional if preferred.

Mind that this module tries to produce the default XDG values and pam_systemd.so might do different, so it's still hacky compared to just using machinectl shell.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .