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 laeuft stuendlich per Cron, laedt die gesamte LDAP-Policy-Ergebnismenge in den Python-Speicher und frisst dabei bis zu 1.8 GB RAM. Der OOM-Killer erschlaegt ihn — und reisst dabei gerne DNS (BIND9/named), Samba und andere Dienste mit.
Wenn dein DC nur 2-4 GB RAM hat (was fuer einen reinen AD/DNS/LDAP-Server voellig ausreicht), hast du ein wiederkehrendes Problem. Und nein, “RAM erhoehen” ist keine Loesung.
TL;DR
- Erstelle einen systemd Timer mit
MemoryMax=768Mals Ersatz fuer den Cron-Job - Deaktiviere den UCR-managed Cron mit dem “31. Februar”-Trick
- Reduziere die Frequenz von stuendlich auf alle 4 Stunden
Der Prozess wird jetzt bei 768 MB per cgroup gekillt — nur er allein, ohne andere Dienste mitzureissen.
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 erwaehnt kein Memory-Leak.
Warum die Standard-Antworten nicht helfen
| “Fix” | Warum es keiner ist |
|---|---|
| RAM erhoehen | Verzoegert das Problem nur. 1.8 GB pro Lauf, stuendlich. |
| Prozess killen | Kommt in einer Stunde wieder |
| Swap vergroessern | Verschiebt den OOM, aber jetzt stehen DNS und Samba im Swap |
swapoff -a | Funktioniert in LXC-Containern nicht (dazu spaeter mehr) |
Die Loesung: 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 auszuloesen.
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. Genuegend Headroom fuer normalen Betrieb, aber weit unter dem Punkt wo der System-OOM zuschlaegt.MemorySwapMax=0: Der Prozess darf keinen Swap benutzen. Verhindert, dass er kalten Code anderer Dienste in den Swap drueckt.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 stuendlich. Auf einem stabilen DC aendern 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 loeschst oder editierst, wird sie beim naechsten ucr commit regeneriert. Das Template generiert die Cron-Zeile aus der UCR-Variable ldap/policy/cron.
Die Loesung: Setze den Cron-Schedule auf den 31. Februar:
ucr set 'ldap/policy/cron=0 0 31 2 *'
Das Template generiert eine syntaktisch gueltige Cron-Zeile, die nie feuert. Die UCR-Variable ueberlebt 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 fuer diesen einen Prozess. DNS, Samba, LDAP laufen weiter.
Bonus-Pitfall: LXC und Swap
Falls dein UCS-DC in einem LXC-Container laeuft (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 aendert sich nicht. Klassisches “works on my machine” — nur dass dein Monitoring das nicht weiss und weiter Alarm schlaegt.
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 zurueck 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 Loesung — sie verschiebt das Problem auf naechste 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 fuer UCR-managed Cron-Jobs — den wirst du oefter brauchen, wenn du UCS-Crons loswerden willst ohne dass ucr commit sie dir wieder hinzaubert.