/run ist ein tmpfs-basiertes Verzeichnis (reiner RAM-Speicher), das Laufzeit-Zustände aktiver Dienste aufnimmt: PID-Files, Unix-Domain-Sockets, Lock-Files, Datenbanken kurzlebiger Sessions. Nach einem Reboot ist es leer — genau das gewünschte Verhalten, weil die Daten nur für laufende Prozesse Sinn ergeben. Auf modernen systemd-Systemen sind /var/run und /var/lock Symlinks auf /run bzw. /run/lock.

Was ist /run?

/run ist seit FHS 3.0 (2015) der offizielle Pfad für Runtime-State — kurzlebige Daten, die nur Sinn ergeben, solange das System läuft. Drei Eigenschaften definieren das Verzeichnis:

  1. tmpfs im RAM, also flüchtig und schnell.
  2. Beim Boot sehr früh verfügbar — noch bevor /var gemountet sein muss.
  3. Pro System eindeutig: /run selbst ist global, /run/user/<uid>/ pro User.
Bash /run als tmpfs erkennen
mount | grep ' /run '
# tmpfs on /run type tmpfs (rw,nosuid,nodev,size=...,mode=755)

# Größe und Belegung
df -h /run
# tmpfs   787M  4.2M  783M   1% /run

# Inhalt auf einen Blick
ls /run/
# NetworkManager   dbus           lock         systemd
# blkid            log            mount        udev
# cron.reboot      lvm            sshd.pid     user

Was du in /run findest:

InhaltBeispiel
PID-Files aktiver Daemons/run/sshd.pid, /run/cron.pid
Unix-Domain-Sockets für IPC/run/dbus/system_bus_socket, /run/docker.sock
Lock-Files/run/lock/<service> (Sticky-Bit gesetzt)
systemd-Unterverzeichnisse/run/systemd/system/, /run/systemd/journal/
udev-Datenbank/run/udev/data/
Pro-User-Runtime-Dirs/run/user/1000/ (für UID 1000)
Mount-Punkt-State/run/mount/, /run/blkid/blkid.tab

Geschichte: von /var/run nach /run

Bis ca. 2012 lagen diese Daten unter /var/run (System) und /var/lock (Locks). Das hatte zwei Probleme:

  1. /var wird erst spät im Boot-Prozess gemountet — Init-Scripts, die schon vorher PID-Files schreiben mussten, hatten ein Henne-Ei-Problem.
  2. /var/run wurde selten als tmpfs gemountet — es konnte beim Crash unaufgeräumte Reste enthalten, die einen Service-Neustart blockierten.

Der Übergang zu systemd (Fedora 15 als erste Distro 2011, Debian 8 als „Jessie" 2015) brachte /run als eigenständiges tmpfs, das vor /var aktiv ist. Aus Kompatibilitäts-Gründen sind die alten Pfade jetzt Symlinks:

Bash Kompatibilitäts-Symlinks ansehen
ls -la /var/run /var/lock
# lrwxrwxrwx 1 root root  4 ... /var/lock -> /run/lock
# lrwxrwxrwx 1 root root  4 ... /var/run  -> /run

# Effekt: dieselben Dateien, zwei Pfade
echo "test" > /tmp/test
ls -la /run/sshd.pid /var/run/sshd.pid
# -rw-r--r-- 1 root root 6 ... /run/sshd.pid
# -rw-r--r-- 1 root root 6 ... /var/run/sshd.pid   ← gleiche Datei

Programme können also /var/run/sshd.pid weiter ansprechen — der Symlink leitet transparent um. Neue Software sollte aber direkt /run/<name>.pid verwenden.

Pro-User: $XDG_RUNTIME_DIR und /run/user/

Eine wichtige Innovation seit systemd: pro Login-User wird ein eigenes Runtime-Verzeichnis angelegt — /run/user/<UID>/. Es gehört dem User mit Mode 0700, sodass nur er hineinschauen darf, und wird bei seinem Logout automatisch entfernt.

Die Standard-Variable $XDG_RUNTIME_DIR zeigt darauf:

Bash Pro-User-Runtime-Dir
echo "$XDG_RUNTIME_DIR"
# /run/user/1000

ls -la "$XDG_RUNTIME_DIR"
# drwx------ 13 michael michael  340 ... .
# ...
# drwxr-xr-x  2 michael michael   80 ... pulse
# srw-rw---- 1 michael michael    0 ... bus           ← dbus-Socket
# srw-rw---- 1 michael michael    0 ... pulse/native  ← PulseAudio
# drwxr-xr-x  3 michael michael   60 ... systemd
# ...

# Gehört uns alleine
stat -c '%a %U:%G' "$XDG_RUNTIME_DIR"
# 700 michael:michael

Was hier alles landet:

  • dbus-Session-Socket für User-Apps
  • PulseAudio-Socket
  • GnuPG-Agent-Socket
  • systemd --user-Sockets und PID-Files
  • Wayland-Display-Socket (wayland-0, wayland-0.lock)

Das ist die richtige Stelle für kurzlebige User-Daten, die nicht persistent sein müssen. Skripte, die einen User-Socket anlegen wollen, gehören dorthin — nicht nach /tmp (wo andere User reinschauen können) und nicht nach ~/.local/state/ (wo der Reboot nichts bringt).

/run/lock und Sticky-Bit

/run/lock (vorher /var/lock) hat das Sticky-Bit gesetzt (Mode 1777), damit User Lock-Files anlegen können, ohne dass andere sie löschen:

Bash /run/lock und seine Aufgabe
ls -ld /run/lock
# drwxrwxrwt 4 root root  80 ... /run/lock

# Klassisches Beispiel: serielle Ports
ls /run/lock/
# subsys/        ← Service-Locks (z. B. cron, exim4)
# LCK..ttyUSB0   ← Lock für seriellen Port

Heute werden Lock-Files seltener manuell genutzt — moderne Programme bevorzugen flock() auf der Original-Datei (atomarer, aufräum-freundlicher). Das /run/lock-Pattern bleibt aber für Legacy-Software und einige Daemons aktiv.

Praxis: Sockets und PID-Files inspizieren

/run ist die erste Anlaufstelle, um zu prüfen, ob ein Service läuft und wie man mit ihm spricht:

Bash PID-Files lesen, um Service-Prozesse zu finden
# PID eines Daemons aus dem File lesen
cat /run/sshd.pid
# 1234

# Prüfen, ob der Prozess wirklich läuft
kill -0 $(cat /run/sshd.pid) && echo "läuft" || echo "tot"

# Was steckt sonst noch in /run?
find /run -maxdepth 2 -name '*.pid' 2>/dev/null
Bash Aktive Sockets in /run
# Alle Unix-Sockets im /run-Baum
find /run -type s 2>/dev/null
# /run/docker.sock
# /run/dbus/system_bus_socket
# /run/systemd/notify
# /run/user/1000/pulse/native
# ...

# Wer hört auf welchem Socket?
sudo lsof -U /run/docker.sock
# COMMAND   PID  USER  FD  TYPE  ...  NAME
# dockerd  1023  root  3u  unix  ...  /run/docker.sock
Bash systemd-Runtime-State
# Aktive Units, die nicht persistent installiert sind
ls /run/systemd/system/
# session-c1.scope.d/  user-1000.slice.d/  ...

# systemd-private Tmp-Verzeichnis pro Service (PrivateTmp=yes)
sudo ls /run/systemd/private-* 2>/dev/null

Eigene Services: in /run schreiben

Wer einen eigenen Service schreibt, sollte PID-File und Socket nach /run/<servicename>/ legen — nicht in /tmp und nicht in /var. systemd hilft dabei mit der RuntimeDirectory=-Direktive:

Bash systemd Unit mit eigenem /run-Verzeichnis
# /etc/systemd/system/myapp.service
[Unit]
Description=Mein Service
After=network.target

[Service]
Type=simple
User=myapp
Group=myapp
ExecStart=/usr/local/bin/myapp --socket /run/myapp/sock --pid /run/myapp/pid

# systemd legt /run/myapp/ automatisch beim Start an,
# gehört User "myapp", Mode 0750 — und entfernt es beim Stop.
RuntimeDirectory=myapp
RuntimeDirectoryMode=0750

[Install]
WantedBy=multi-user.target

Vorteile dieser Direktive:

  • Automatisches Anlegen: /run/myapp/ existiert beim Start, ohne dass das Programm es selbst tun muss.
  • Saubere Berechtigungen: gehört dem konfigurierten User, nicht root.
  • Automatisches Entfernen: beim Service-Stop wird das Verzeichnis entfernt — kein zurückbleibender Müll.

Interessantes

/var/run-Pfade in alter Doku einfach umlenken

Wer in alten Tutorials oder Manpages auf /var/run/sshd.pid stößt, muss nicht panisch werden — der Symlink-Pfad funktioniert weiter. Für eigene neue Skripte aber direkt /run/sshd.pid verwenden.

$XDG_RUNTIME_DIR ist nur in interaktiven Sessions gesetzt

Bei Cron-Jobs oder systemd-Units, die ohne Login-Session laufen, ist $XDG_RUNTIME_DIR leer. Skripte, die sie verwenden, sollten einen Fallback haben — etwa /run/user/$(id -u) oder mktemp -d als Notlösung. Sonst schlägt der Skript-Lauf in der Cron-Umgebung still fehl.

/run wächst nicht — bis es es tut

tmpfs ist standardmäßig 10–20 % des RAMs als Limit. Auf Systemen mit sehr vielen Services oder vielen User-Sessions kann sich /run füllen — typischerweise durch übergroße Logfiles in /run/log/ oder vergessene Drop-In-Verzeichnisse. df -h /run und du -sh /run/* regelmäßig prüfen.

PID-Files sind ein Race-Risiko

Klassisches Pattern: Skript liest PID aus File, prüft mit kill -0, fängt Race-Condition. Wenn der Prozess in dem Moment endet und seine PID neu vergeben wird, „tötet" das Skript einen unbeteiligten Prozess. Sicherer: systemd's MainPID direkt aus systemctl show lesen oder flock für Lock-Files verwenden.

Wayland-Sockets liegen nur in /run/user/

Das wayland-0-Socket eines Wayland-Servers liegt unter $XDG_RUNTIME_DIR/wayland-0. Wer eine Wayland-Anwendung von einer anderen User-Session aus starten will (etwa ein anderer User per sudo -u), muss XDG_RUNTIME_DIR und WAYLAND_DISPLAY korrekt mit setzen — sonst sieht die Anwendung kein Display.

Container haben ein eigenes /run

Im Container-Namespace ist /run ein eigenes tmpfs, völlig getrennt vom Host. Sockets sind also nicht direkt sichtbar — wer Docker-Socket-Zugriff im Container braucht, mountet /run/docker.sock explizit per -v /run/docker.sock:/run/docker.sock. Das ist ein erheblicher Sicherheits-Risiko (Container kann den Host steuern), wird aber für CI-Tools verbreitet genutzt.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Verzeichnisstruktur

Zur Übersicht