Du betreibst einen UCS 5 Domain Controller und alle paar Stunden passiert das:

oom-kill: task=univention-poli, pid=93824
Memory cgroup out of memory: Killed process 93824 (univention-poli)
  total-vm:1853704kB, anon-rss:1810820kB

Der Prozess univention-policy-update-config-registry läuft stündlich per Cron, lädt die gesamte LDAP-Policy-Ergebnismenge in den Python-Speicher und frisst dabei bis zu 1.8 GB RAM. Der OOM-Killer erschlägt ihn — und reißt dabei gerne DNS (BIND9/named), Samba und andere Dienste mit.

Wenn dein DC nur 2-4 GB RAM hat (was für einen reinen AD/DNS/LDAP-Server völlig ausreicht), hast du ein wiederkehrendes Problem. Und nein, “RAM erhöhen” ist keine Lösung.

TL;DR

  1. Erstelle einen systemd Timer mit MemoryMax=768M als Ersatz für den Cron-Job
  2. Deaktiviere den UCR-managed Cron mit dem “31. Februar”-Trick
  3. Reduziere die Frequenz von stündlich auf alle 4 Stunden

Der Prozess wird jetzt bei 768 MB per cgroup gekillt — nur er allein, ohne andere Dienste mitzureißen.

Warum das passiert

Das Python-Script /usr/lib/univention-directory-policy/univention-policy-update-config-registry ruft ucr_policy_result() auf, das die komplette Policy-Ergebnismenge aus LDAP in ein Python-Dictionary materialisiert. Keine Pagination, kein Streaming, kein Memory-Limit.

Die Aufrufkette:

Cron (5 * * * *)
  -> /usr/share/univention-directory-policy/univention-directory-policy-cron
    -> jitter 300 (zufaellige Verzoegerung bis 5 Min)
      -> run-parts /usr/lib/univention-directory-policy/
        -> univention-policy-update-config-registry  <-- hier leakt es

Stand April 2026 gibt es keinen Upstream-Fix (Paketversion 11.0.1-1A ist aktuell). Der Changelog erwähnt kein Memory-Leak.

Warum die Standard-Antworten nicht helfen

“Fix”Warum es keiner ist
RAM erhöhenVerzögert das Problem nur. 1.8 GB pro Lauf, stündlich.
Prozess killenKommt in einer Stunde wieder
Swap vergrößernVerschiebt den OOM, aber jetzt stehen DNS und Samba im Swap
swapoff -aFunktioniert in LXC-Containern nicht (dazu später mehr)

Die Lösung: systemd Timer mit MemoryMax

Die Idee: Ersetze den Cron-Job durch einen systemd Timer. systemd kann per cgroup ein Memory-Limit setzen — der Prozess wird bei 768 MB gekillt, nur er allein, ohne den System-OOM-Killer auszulösen.

Schritt 1: Service erstellen

cat > /etc/systemd/system/univention-policy-update.service << 'EOF'
[Unit]
Description=Univention Policy Update Config Registry (memory-limited)
After=slapd.service

[Service]
Type=oneshot
ExecStart=/usr/share/univention-directory-policy/univention-directory-policy-cron
MemoryMax=768M
MemorySwapMax=0
TimeoutStartSec=900
EOF

Was die Werte bedeuten:

  • MemoryMax=768M: Prozess wird per cgroup bei 768 MB gekillt. Genügend Headroom für normalen Betrieb, aber weit unter dem Punkt, wo der System-OOM zuschlägt.
  • MemorySwapMax=0: Der Prozess darf keinen Swap benutzen. Verhindert, dass er kalten Code anderer Dienste in den Swap drückt.
  • TimeoutStartSec=900: 15 Minuten — das Script hat einen eingebauten Jitter von bis zu 300 Sekunden.

Schritt 2: Timer erstellen

cat > /etc/systemd/system/univention-policy-update.timer << 'EOF'
[Unit]
Description=Univention Policy Update Timer (replaces cron)

[Timer]
OnCalendar=*-*-* 01,05,09,13,17,21:05:00
RandomizedDelaySec=300
Persistent=true

[Install]
WantedBy=timers.target
EOF

Alle 4 Stunden statt stündlich. Auf einem stabilen DC ändern sich UCR-Policies nicht jede Stunde. Persistent=true stellt sicher, dass ein verpasster Lauf nach einem Reboot nachgeholt wird.

Schritt 3: Aktivieren

systemctl daemon-reload
systemctl enable --now univention-policy-update.timer

Schritt 4: UCR-Cron deaktivieren

Hier wird es elegant. Die Cron-Datei /etc/cron.d/univention-directory-policy ist UCR-managed — wenn du sie löschst oder editierst, wird sie beim nächsten ucr commit regeneriert. Das Template generiert die Cron-Zeile aus der UCR-Variable ldap/policy/cron.

Die Lösung: Setze den Cron-Schedule auf den 31. Februar:

ucr set 'ldap/policy/cron=0 0 31 2 *'

Das Template generiert eine syntaktisch gültige Cron-Zeile, die nie feuert. Die UCR-Variable überlebt ucr commit und Paket-Updates. Kein Template-Hack, kein dpkg-divert, kein chmod -x.

Verifizierung

# Timer aktiv?
systemctl list-timers univention-policy-update.timer

# Memory-Limits gesetzt?
systemctl show univention-policy-update.service -p MemoryMax,MemorySwapMax
# -> MemoryMax=805306368 (768 MB)
# -> MemorySwapMax=0

# Cron tot?
ucr get ldap/policy/cron
# -> 0 0 31 2 *

# Manueller Test:
systemctl start univention-policy-update.service
journalctl -u univention-policy-update.service --no-pager -n 20

Wenn der Prozess bei 768 MB gekillt wird, siehst du exit code 137 (SIGKILL) — aber nur für diesen einen Prozess. DNS, Samba, LDAP laufen weiter.

Bonus-Pitfall: LXC und Swap

Falls dein UCS-DC in einem LXC-Container läuft (Proxmox): swapoff -a && swapon -a hat dort keine Wirkung.

Der Swap in einem LXC-Container ist “virtual” — Typ none in swapon -s. Die Swap-Pages werden vom Host per cgroup verwaltet, nicht vom Container. swapoff meldet brav Erfolg (exit 0), aber die Swap-Belegung ändert sich nicht. Klassisches “works on my machine” — nur dass dein Monitoring das nicht weiß und weiter Alarm schlägt.

Stale Swap-Pages altern mit der Zeit raus oder werden beim Container-Reboot freigegeben. MemorySwapMax=0 im systemd Service verhindert, dass der Policy-Prozess neuen Swap belegt.

Rollback

systemctl disable --now univention-policy-update.timer
rm /etc/systemd/system/univention-policy-update.{service,timer}
systemctl daemon-reload
ucr set 'ldap/policy/cron=5 * * * *'

Drei Befehle und du bist zurück beim Original. Der OOM-Killer freut sich.

Fazit

Univention hat das Memory-Leak seit mindestens UCS 5.0-9 nicht gefixt. Die Standard-Antwort “mehr RAM” ist keine Lösung — sie verschiebt das Problem auf nächste Woche, wenn du statt 2 GB halt 4 GB Swap im Container hast.

systemd cgroups mit MemoryMax sind der saubere Weg: der Prozess wird isoliert gekillt, der Rest des Systems bleibt stabil. Und der “31. Februar”-Trick für UCR-managed Cron-Jobs — den wirst du öfter brauchen, wenn du UCS-Crons loswerden willst, ohne dass ucr commit sie dir wieder hinzaubert.