User runtime directories on modern Linux, aka /run/user/<uid>

December 15, 2021

If you've used a "modern" Linux, which is to say a Linux running systemd, you may have noticed one or more /run/user/<uid> directory hierarchies. These are especially noticeable on systems with a bunch of users logged into them (such as many of our servers), because each of these directory trees is a separate tmpfs mount. These are a systemd feature that's generally called a "user runtime directory".

The mounts and so on are normally created (and destroyed) through pam_systemd, assuming that it's part of your PAM stack and you're running all of the relevant systemd bits. I believe that the actual work may be done behind the scenes by systemd's logind stuff, with pam_systemd calling out to it to start things. This PAM module also sets up $XDG_RUNTIME_DIR to point into the user's runtime directory. At least some of the time, you can also wind up with at least a $DBUS_SESSION_BUS_ADDRESS environment variable that points into your user runtime directory.

(On sufficiently recent systemd based systems, there's also the user-runtime-dir systemd service, which I think logind uses to actually do the work of creating the user runtime directory. Our Ubuntu 20.04 servers and my Fedora machines have this service, but our 18.04 machines don't.)

To generalize, the purpose of the user runtime directory is to replace all of the various traditional uses of /tmp for session related files, subdirectories, and so on. A locked down, automatically managed per-user directory tree for this purpose is more secure and also allows for nicer naming of things; your DBUS session bus can just be at /run/user/<uid>/bus instead of, say, /tmp/dbus-<big jumble>. This also provides systemd with a place to put its own per-user stuff, such as some user systemd session information.

As you can see from looking at your own /run/user directory, any number of things have been updated to put their files there on a modern Linux machine. This happens both for desktop logins and for remote logins, especially if you have a relatively full package set installed; our Ubuntu 20.04 servers give me a healthy collection of running services and /run/user entries even on SSH logins. I don't know what all of these updated programs do on systems that don't use systemd and may not have user runtime directories. There can also be mysterious FUSE filesystems mounted there, like /run/user/<uid>/doc on my Fedora 24 machine.

(It's my impression that programs make more user runtime directories and use less obvious names than they might have in the /tmp era. Or perhaps not; our /tmp directories are full of oddly named clutter too, even today.)

You're probably free to use your user runtime directory for your own programs, or just for things in general. It does have size limits, and since it's a tmpfs, the space you use up is coming out of your own RAM. But watch out; at least one thing feels free to dump a potentially large log file into your /run/user directory.

(This behavior wasn't really great even in the days when it might be writing that log to /tmp. It's even less so today.)

Comments on this page:

From at 2021-12-16 03:01:13:

One of the reasons each UID gets their own tmpfs mount (instead of sharing the parent /run tmpfs) is so that they could have individual size= limits. Apparently the limit is configurable through logind.conf. Originally the runtime directory was supposed to be the per-user equivalent of /var/run – i.e. to hold sockets, pipes, flag files, and other such IPC-related things that occupy little to no space (definitely not for log files; that should go to ~/.cache if not /dev/null).

Aside from cleaning up /tmp (similar to the system-wide migration of /var/run/foo, /tmp/foo, /dev/.foo, /dev/shm/.foo, /lib/init/rw/foo… towards a neat and tidy /run), predictable naming to reduce the reliance on environment variables was one big reason; for example, D-Bus client libraries have been patched so that they'll look for /run/user/<uid>/bus by default if the corresponding environment variable is not set. There were patches to do the same for libX11 as well, but they fizzled out.

(GnuPG with its gpg-agent has taken this to an extreme and made it incredibly difficult to not use the runtime directory for gpg-agent sockets.)

The doc FUSE mount is something to do with Flatpak's "Documents portal" – if I remember correctly, it's how sandboxed apps are given access to individual files (the "Open" dialog runs on the host and lets you select the file which is then exposed via FUSE to guest).

(The gvfs mount just exposes GNOME's GVFS userspace filesystem connections, so that e.g. smb:// or sftp:// "mounts" can be usable to non-GTK apps. KDE now has an equivalent as well, probably at kio-fuse.)

IIRC, runtime dir creation was moved out to user-runtime-dir@ service so that user@.service would behave nicely if someone started it manually and not through pam_systemd. (Previously the only way to get a runtime dir was to have a logind session, or use its linger mechanism.)

I'm not sure how all of this works on non-systemd distros, although I think at least elogind will just create the runtime dir in the same way (i.e. pam_elogind), and I've seen a pam_xdg module somewhere around that's supposed to do the same in a more lightweight way.

In general, though, if the runtime dir isn't present, some programs will fall back to the user's cache directory (~/.cache), some will fall back to /tmp, some functionality won't exist at all, some will revert to the older implementation (e.g. the "user bus" is implemented via systemd --user having dbus.service, so if there's no systemd then there's no per-user dbus.service and you just get the traditional behavior of running dbus-launch and creating /tmp/dbus-XXXXXX sockets). Out of curiosity I checked what the Wayland client libraries do, and it seems they'll insist on WAYLAND_DISPLAY pointing to an absolute path in that situation.

Written on 15 December 2021.
« Finding Ubuntu (and Debian) packages that are in odd states
In Go 1.18, generics are implemented through code specialization »

Page tools: View Source, View Normal, Add Comment.
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Wed Dec 15 23:18:47 2021
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.