Server-Administration

Linux Patchmanagement automatisieren, ohne den Betrieb zu stoppen

Ab etwa hundert Hosts ist der monatliche Patchtag keine Betriebsform mehr, sondern ein Risiko. Was trägt, ist automatisiertes Patchen in Ringen mit definierten Wartungsfenstern und einem Rückweg, der auch nachts funktioniert.

6 Kapitel mit allen Befehlen
Administrator steckt ein Netzwerkkabel in einen Serverschrank
KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt
Seit 1997 am Markt Präsenz & Live-Online 4,9 aus 503 Google-Bewertungen Auch Inhouse für dein Team
Worum es geht

Warum der manuelle Patchtag an der Flotte scheitert

Auf einem einzelnen Server ist ein Update trivial. Bei 300 Systemen über Debian, Ubuntu, RHEL und SLES hinweg wird daraus ein Koordinationsproblem: unterschiedliche Paketmanager, unterschiedliche Wartungsfenster, Abhängigkeiten zwischen Datenbank, Applikationsserver und Reverse Proxy. Wer das per SSH-Schleife und Kalendereintrag löst, verschiebt Updates so lange, bis ein CVE mit hohem CVSS-Score die Entscheidung erzwingt.

Der zweite Treiber ist regulatorisch. NIS2, ISO 27001 und Kundenaudits fragen nicht nur, ob gepatcht wird, sondern in welcher Frist und wie das belegt ist. Ein Prozess, der von der Verfügbarkeit einzelner Kolleginnen und Kollegen am Samstag abhängt, ist weder reproduzierbar noch nachweisbar.

Typische Fehler sehen wir immer wieder in derselben Reihenfolge: unattended-upgrades wird aktiviert, aber niemand liest die Mails, deshalb bleibt der ausstehende Reboot monatelang liegen und der Kernel im Speicher ist Jahre alt. Oder dnf-automatic läuft mit apply auf einem Datenbankcluster und startet mitten im Tagesgeschäft Dienste neu. Oder es gibt gar keine Staffelung, sodass ein fehlerhaftes Paket alle Knoten einer HA-Gruppe gleichzeitig trifft. Automatisierung ohne definierte Ringe und ohne Rollback-Pfad vergrößert den Schaden, statt ihn zu begrenzen.

Miniatur-Szene: geöffneter Serverschrank mit Werkzeug, Patchpanel und grünem Uptime-Balken
KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt

Vom Advisory bis zum bestätigten Reboot

  1. 01 Inventar und Patchquelle festlegen
  2. 02 Testring automatisch patchen
  3. 03 Produktion gestaffelt ausrollen
  4. 04 Reboot-Bedarf prüfen und einplanen
  5. 05 Dienste verifizieren, sonst Rollback
  6. 06 Patchstand berichten und dokumentieren
Was du mitnimmst

So sieht ein tragfähiger Patchprozess aus

Automatisiertes Patchmanagement besteht aus drei Schichten: der lokale Mechanismus auf dem einzelnen Host, die Orchestrierung über die Flotte und die Entscheidung, wann ein Reboot stattfindet. Die folgenden Punkte beschreiben, worauf es dabei technisch ankommt.

Lokale Automatik sauber konfigurieren

Unter Debian und Ubuntu steuerst du unattended-upgrades über /etc/apt/apt.conf.d/50unattended-upgrades und beschränkst die Origins in der Regel auf die Security-Suite. Unter RHEL, Rocky und AlmaLinux übernimmt dnf-automatic die Rolle, wobei der Unterschied zwischen download-only und apply die eigentliche Risikoentscheidung ist. Auf SLES erledigt das zypper-Backend in Verbindung mit dem Multi-Linux Manager denselben Job.

Flotten über Ansible in Ringen ausrollen

Ein Playbook mit ansible.builtin.package oder den distributionsspezifischen Modulen, kombiniert mit serial und max_fail_percentage, gibt dir kontrollierte Wellen: erst Testsysteme, dann unkritische Produktion, dann die Kernsysteme. Über pre_tasks und post_tasks nimmst du Knoten aus dem Loadbalancer, setzt Monitoring-Downtimes und prüfst anschließend den Dienststatus, bevor der nächste Knoten drankommt.

Reboots als eigenen, bewussten Schritt behandeln

Paketupdate und Neustart gehören getrennt. Ob ein Reboot nötig ist, verrät /var/run/reboot-required unter Debian oder needs-restarting -r unter RHEL. Mit dem reboot-Modul und definierten Wartebedingungen läuft der Neustart kontrolliert, bei Clustern erst nach Prüfung von Quorum und Ressourcenverteilung. Kernel Live Patching über kpatch oder das SUSE-Pendant verschiebt den Neustart, ersetzt ihn aber nicht dauerhaft.

Ausnahmen bewusst definieren statt stillschweigend zulassen

Datenbanken, Reverse Proxies, SAP-Systeme und Appliances gehören in eine eigene Gruppe mit engerem Wartungsfenster und manueller Freigabe. Versionspins über dnf versionlock oder apt-mark hold sind legitim, brauchen aber ein Ablaufdatum und einen Verantwortlichen, sonst wachsen sie zu unsichtbaren Altlasten heran.

Rollback und Snapshots vorher testen

Auf btrfs mit snapper unter SLES oder über Storage-Snapshots der Virtualisierung hast du einen echten Rückweg. dnf history undo hilft bei Paketproblemen, nicht bei Datenbankmigrationen. Wichtig ist, dass der Rollback-Pfad einmal geübt wurde, bevor du ihn nachts unter Druck zum ersten Mal ausprobierst.

Ergebnis messbar machen

Ein Report über verbleibende offene Updates, Patchalter pro Host und Zeit bis zum Reboot gehört ins Monitoring, etwa über Prometheus-Metriken oder die Inventarfunktionen des Multi-Linux Managers. Erst damit siehst du die Systeme, die aus der Automatik herausgefallen sind, und genau die sind erfahrungsgemäß das Problem.

Tutorial

Von der Bestandsaufnahme zum Patchring

Der Prozess entsteht hier in der Reihenfolge, in der er auch im Betrieb wächst: erst messen, dann die lokale Automatik auf beiden Distributionsfamilien einstellen, dann die Flotte in Ringen fahren, Reboots getrennt steuern und zum Schluss den Rückweg und den Nachweis absichern.

01

Erst zählen, dann automatisieren

Bevor du irgendetwas automatisierst, brauchst du eine Zahl: Wie viele Hosts hängen wie weit zurück, und wie viele davon warten auf einen Neustart? Ohne diesen Ausgangswert kannst du später nicht belegen, dass die Automatik überhaupt wirkt.

Ausgangswert je Host ermitteln
# RHEL / Rocky / Alma
dnf check-update --refresh          # Exitcode 100 = Updates offen, 0 = nichts zu tun
dnf updateinfo list --security
dnf needs-restarting -r             # Exitcode 1 = Neustart nötig

# Debian / Ubuntu
sudo apt-get update -qq
apt-get -s dist-upgrade | grep -c '^Inst'
[ -f /var/run/reboot-required ] && cat /var/run/reboot-required.pkgs

Der interessante Teil sind die Exitcodes, nicht die Textausgabe. dnf check-update liefert 100, wenn Updates anstehen, und 0, wenn nicht. Damit baust du einen Monitoring-Check, ohne Ausgaben zu parsen, die sich mit der nächsten Version ändern. needs-restarting kommt aus dnf-plugins-core, das musst du auf minimalen Installationen nachziehen.

02

dnf-automatic und unattended-upgrades richtig einstellen

Die lokale Automatik deckt die breite Masse ab. Einen gesteuerten Rollout ersetzt sie nicht, aber ohne sie fällt ein vergessener Host über Jahre zurück, ohne dass es jemandem auffällt.

/etc/dnf/automatic.conf (RHEL / Rocky / Alma)
[commands]
upgrade_type = default
random_sleep = 3600
download_updates = yes
apply_updates = yes
reboot = never

[emitters]
emit_via = stdio

random_sleep verhindert, dass hundert Hosts gleichzeitig auf denselben Spiegelserver einschlagen. reboot = never ist bewusst gesetzt: Neustarts steuerst du getrennt, nicht als Nebenwirkung eines Paketupdates.

Timer aktivieren und ins Wartungsfenster legen
sudo systemctl enable --now dnf-automatic.timer
systemctl list-timers dnf-automatic.timer
sudo systemctl edit dnf-automatic.timer
# [Timer]
# OnCalendar=
# OnCalendar=Tue,Thu 02:30
# RandomizedDelaySec=1800

journalctl -u dnf-automatic.service -n 50 --no-pager

Die leere Zeile OnCalendar= vor dem eigenen Wert ist Pflicht, sonst kommt dein Termin zum vorhandenen dazu statt ihn zu ersetzen. Aktiviere außerdem genau einen Timer: Neben dnf-automatic.timer gibt es spezialisierte Varianten wie dnf-automatic-install.timer, die das Verhalten aus der automatic.conf überschreiben. Laufen zwei parallel, gilt nicht mehr das, was in der Konfigurationsdatei steht.

/etc/apt/apt.conf.d/50unattended-upgrades (Debian / Ubuntu, Auszug)
Unattended-Upgrade::Origins-Pattern {
        "origin=Debian,codename=${distro_codename},label=Debian-Security";
};
Unattended-Upgrade::Automatic-Reboot "false";
Unattended-Upgrade::MailReport "on-change";
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";

// /etc/apt/apt.conf.d/20auto-upgrades
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";

Unter Ubuntu lautet das Muster origin=Ubuntu,codename=${distro_codename}-security statt der Debian-Variante. Prüf es mit sudo unattended-upgrade --dry-run --debug: Der Trockenlauf zeigt Paket für Paket, was die Automatik nehmen würde und was sie warum überspringt. Das ist der einzige verlässliche Weg, ein Origins-Pattern zu kontrollieren. Das Protokoll liegt unter /var/log/unattended-upgrades/.

Dieselbe Aufgabe, zwei Welten

AufgabeRHEL / Rocky / AlmaDebian / Ubuntu
Automatik-Paketdnf-automaticunattended-upgrades
Konfigurationsdatei/etc/dnf/automatic.conf/etc/apt/apt.conf.d/50unattended-upgrades
Zeitsteuerungdnf-automatic.timerapt-daily-upgrade.timer
Nur Sicherheitsupdatesupgrade_type = securityOrigins-Pattern auf -security begrenzen
Neustart nötig?dnf needs-restarting -r/var/run/reboot-required
Dienste mit alten Bibliothekendnf needs-restarting -sneedrestart -b
Paket festhaltendnf versionlock addapt-mark hold
Transaktion zurücknehmendnf history undokein Gegenstück, Snapshot nötig
03

Ringe mit Ansible: serial, limit und Abbruchkriterium

Ringe sind keine Ordnerstruktur, sondern Gruppen im Inventar. Der Kanarienvogel-Ring ist bewusst klein und enthält Systeme, deren Ausfall du am Vormittag verkraftest.

inventory.yml mit Patchringen
all:
  children:
    ring1_kanarien:
      hosts:
        web01.example.internal:
    ring2_test:
      hosts:
        web02.example.internal:
        app01.example.internal:
    ring3_produktion:
      hosts:
        web03.example.internal:
        db01.example.internal:

Ein Host darf in mehreren Gruppen stehen, aber genau in einem Ring. Wer das nicht sauber trennt, patcht Datenbank und zugehörigen Anwendungsserver in derselben Welle und hat dann beide gleichzeitig weg.

patch.yml, distributionsübergreifend
---
- name: Patchlauf
  hosts: "{{ ziel_ring }}"
  become: true
  serial: "25%"
  max_fail_percentage: 0

  tasks:
    - name: Paketstand vorher sichern
      ansible.builtin.shell:
        cmd: set -o pipefail && rpm -qa | sort > /var/log/pakete-vorher.txt
      changed_when: false
      when: ansible_facts['os_family'] == 'RedHat'

    - name: Updates einspielen (RedHat)
      ansible.builtin.dnf:
        name: '*'
        state: latest
        security: true
      when: ansible_facts['os_family'] == 'RedHat'

    - name: Updates einspielen (Debian)
      ansible.builtin.apt:
        upgrade: dist
        update_cache: true
        cache_valid_time: 3600
      when: ansible_facts['os_family'] == 'Debian'

serial: „25%“ teilt jeden Ring nochmals in Wellen auf, max_fail_percentage: 0 bricht den kompletten Lauf ab, sobald ein Host in einer Welle scheitert. Ohne diese zweite Bremse patcht Ansible fröhlich weiter, während sich der erste Host schon nicht mehr meldet.

Ring für Ring, mit Freigabe dazwischen
ansible-playbook patch.yml -e ziel_ring=ring1_kanarien --check --diff
ansible-playbook patch.yml -e ziel_ring=ring1_kanarien

# Erst nach der Freigabe und einer beobachteten Nacht:
ansible-playbook patch.yml -e ziel_ring=ring2_test
ansible-playbook patch.yml -e ziel_ring=ring3_produktion

Zwischen den Ringen gehört eine Wartezeit, in der Monitoring und Fachbereich Zeit hatten, etwas zu bemerken. Vierundzwanzig Stunden zwischen Ring 1 und Ring 2 sind ein brauchbarer Startwert, den du nach den ersten Durchläufen anpasst.

04

Reboots steuern statt hoffen

Der Neustart ist der Teil, der wehtut, deshalb bekommt er einen eigenen Schritt mit eigener Entscheidung. Ein Patchlauf, der ungefragt neu startet, verbrennt mehr Vertrauen als jede offene Lücke.

Neustartbedarf ermitteln und nur dann handeln
- name: Neustartbedarf ermitteln (RedHat)
  ansible.builtin.command: dnf needs-restarting -r
  register: nr
  changed_when: false
  failed_when: nr.rc not in [0, 1]
  when: ansible_facts['os_family'] == 'RedHat'

- name: Neustartmarker prüfen (Debian)
  ansible.builtin.stat:
    path: /var/run/reboot-required
  register: rr
  when: ansible_facts['os_family'] == 'Debian'

- name: Neustart im Wartungsfenster
  ansible.builtin.reboot:
    reboot_timeout: 900
    post_reboot_delay: 30
    msg: "Neustart nach Patchlauf"
  when: (nr.rc | default(0)) == 1 or (rr.stat.exists | default(false))

failed_when: nr.rc not in [0, 1] ist der entscheidende Punkt. needs-restarting -r benutzt den Exitcode als Aussage, nicht als Fehlermeldung. Ohne diese Zeile bricht das Playbook ausgerechnet bei den Hosts ab, die einen Neustart brauchen.

05

Rückweg: dnf history, Snapshots, Versionslock

Der Rückweg entscheidet, ob du nachts ruhig schläfst. Er unterscheidet sich zwischen den Distributionsfamilien stärker als alles andere in diesem Prozess.

Transaktionen und Ausnahmen
# RHEL-Familie: Transaktionen sind nachvollziehbar und umkehrbar
dnf history list | head
dnf history info 42
sudo dnf history undo 42

# Einzelnes Paket bewusst festhalten
sudo dnf install -y python3-dnf-plugin-versionlock
sudo dnf versionlock add postgresql-server
dnf versionlock list

# Debian / Ubuntu
sudo apt-mark hold postgresql-16
apt-mark showhold

dnf history undo hat Grenzen: Pakete, die inzwischen aus dem Repository verschwunden sind, lassen sich nicht zurückholen, und Daten- oder Formatmigrationen macht kein Paketmanager rückgängig. Unter Debian gibt es überhaupt kein Gegenstück. Der eigentliche Rückweg ist deshalb der Snapshot, nicht der Paketmanager.

Snapshot vor dem Lauf, Rückrollen danach
# LVM
sudo lvcreate -s -L 5G -n root_vor_patch /dev/vgsystem/root
sudo lvconvert --merge /dev/vgsystem/root_vor_patch   # wirkt beim nächsten Neustart
sudo lvremove /dev/vgsystem/root_vor_patch            # nach erfolgreichem Lauf

# Btrfs mit snapper
sudo snapper -c root create --description "vor Patchlauf"
sudo snapper -c root list

Ein LVM-Snapshot ist kein Backup. Läuft er voll, weil in der Volume Group zu wenig freier Platz reserviert wurde, wird er ungültig, und das merkst du erst beim Zurückrollen. Plan den erwarteten Änderungsumfang plus Reserve ein und lösch den Snapshot nach dem erfolgreichen Patchlauf, sonst bremst er die Schreiblast dauerhaft aus.

Bevor der erste automatisierte Ring in Produktion läuft

  • Wartungsfenster je Ring dokumentiert und im Timer hinterlegtEin Patchlauf um 14 Uhr trifft die Fachabteilung, einer um 3 Uhr nur den Bereitschaftsdienst. Wer das Fenster nicht festlegt, bekommt es vom Zufall zugeteilt.
  • Rückweg auf einem echten Host einmal vollständig durchgespieltEin Rollback, den niemand je ausgeführt hat, ist eine Vermutung. Nachts um vier willst du keine Vermutung, sondern eine Anleitung, die schon einmal funktioniert hat.
  • Ausnahmen mit Ablaufdatum und benannter Verantwortlichkeit versehenEin versionlock ohne Datum bleibt jahrelang stehen und wird zur unbemerkten Altlast. Genau diese Hosts findet dann der Schwachstellenscan, oder ein Angreifer.
  • Monitoring meldet Hosts, die mehrere Läufe in Folge verpasst habenDer gefährlichste Host ist nicht der, bei dem das Patchen fehlschlägt, sondern der, bei dem es niemand versucht hat, weil er in keiner Gruppe steht.
  • Neustartverhalten überall ausdrücklich gesetzt statt auf Standardwerte vertrautDie Voreinstellungen unterscheiden sich zwischen den Distributionen. Ein unerwarteter Neustart mitten am Tag kostet mehr Vertrauen, als der Patch an Sicherheit bringt.
06

Nachweis: den Patchstand sichtbar machen

Was du nicht messen kannst, wirst du im Audit nicht belegen und in der Budgetrunde nicht verteidigen können. Eine einzelne Metrik je Host reicht dafür aus.

/usr/local/bin/patch-metrik.sh
#!/usr/bin/env bash
set -euo pipefail

OUT=/var/lib/node_exporter/textfile_collector/patch.prom
TMP="$OUT.tmp"

if command -v dnf >/dev/null; then
  OFFEN=$(dnf -q check-update 2>/dev/null | grep -c . || true)
else
  OFFEN=$(apt-get -s dist-upgrade 2>/dev/null | grep -c '^Inst' || true)
fi

printf 'offene_updates %s\n' "$OFFEN" > "$TMP"
mv "$TMP" "$OUT"

Das mv am Ende ist kein Schönheitsdetail. Der Textfile-Collector liest die Datei nebenläufig, und eine halb geschriebene Datei liefert kaputte Werte. Erst in eine temporäre Datei schreiben und dann atomar umbenennen löst das. Hänge das Skript an einen systemd-Timer, nicht an den Patchlauf, sonst misst du nur nach dem Patchen.

Drei Kennzahlen, die im Bericht wirklich zählen

  • Anteil der Hosts, deren letzter erfolgreicher Patchlauf jünger ist als das vereinbarte Intervall
  • Zeit zwischen der Veröffentlichung einer kritischen Lücke und dem Abschluss von Ring 3
  • Anzahl aktiver Ausnahmen, aufgeschlüsselt nach Verantwortlichkeit und Ablaufdatum
Gut zu wissen

Häufige Fragen zu automatisiertem Patchmanagement

Noch etwas offen? Wir sind ohne Warteschleife für dich da.

Frag uns direkt
Darf man unattended-upgrades auf Produktivservern aktivieren?
Für reine Security-Updates auf zustandslosen Systemen wie Webfrontends oder Applikationsknoten hinter einem Loadbalancer ist das gängige Praxis, sofern automatische Reboots deaktiviert bleiben und der Reboot separat orchestriert wird. Bei Datenbanken, Clusterknoten und Systemen mit herstellergebundenen Versionsständen solltest du bei download-only bleiben und die Installation aktiv freigeben.
Was ist der Unterschied zwischen dnf-automatic und einem Ansible-Playbook?
dnf-automatic ist ein lokaler Timer auf einem einzelnen Host und weiß nichts über andere Systeme. Ein Ansible-Playbook orchestriert die Flotte, kann Knoten nacheinander abarbeiten, Loadbalancer und Monitoring einbeziehen und bei Fehlern abbrechen. In der Praxis kombiniert man beides: lokale Automatik für Security-Pakete auf unkritischen Systemen, Ansible für alles, was Reihenfolge und Prüfungen braucht.
Macht Kernel Live Patching Reboots überflüssig?
Nein. Live Patching schließt Sicherheitslücken im laufenden Kernel und verschafft dir Zeit bis zum nächsten regulären Wartungsfenster. Größere Kernel-Sprünge, Firmware- und Microcode-Updates sowie Änderungen an glibc oder systemd erfordern weiterhin einen Neustart, und auch die Live-Patch-Kette braucht irgendwann einen Basiswechsel.
Wie oft sollte gepatcht werden?
Statt einer festen Zahl arbeitest du besser mit Fristen nach Kritikalität, zum Beispiel kurzfristig für aktiv ausgenutzte Schwachstellen und ein regelmäßiger Zyklus für den Rest. Entscheidend ist, dass die Frist im Prozess hinterlegt ist und du über einen Report jederzeit sehen kannst, welche Hosts sie reißen.
Welcher cmt-Kurs passt zum Aufbau eines automatisierten Patchprozesses?
Für den Einstieg in die Orchestrierung eignet sich der Ansible Kompaktkurs, für saubere Rollen, Fehlerbehandlung und größere Umgebungen der Kurs Ansible für Fortgeschrittene: Kein Playbook-Chaos. Im Red-Hat-Umfeld deckt AU294 Red Hat Enterprise Linux Automation with Ansible dieselbe Aufgabe herstellernah ab, im SUSE-Umfeld ist SUSE Multi-Linux Manager 5 Operations der passende Weg. Alle Kurse gibt es bei cmt auch Live-Online.

Zuletzt geprüft am 26. Juli 2026.

Das sagen Teilnehmer

Echte Stimmen aus unseren IT-Kursen

Qualitativ sehr guter Kurs. Ruhiger und wertschätzender Umgang. Keine Informationsüberlastung.
Rückmeldung aus dem Kurs „Ansible Kompaktkurs“
Sehr kompetenter Dozent der gut auf alle Fragen eingegangen ist.
Rückmeldung aus dem Kurs „Kubernetes Grundkurs“
Persönlich für dich da

Deine Ansprechpartner

Du bist dir nicht sicher, welcher Kurs oder welches Level zu dir passt? Wir beraten dich persönlich und kostenlos.

Yves Hoppe

Yves Hoppe

Weiterbildung & Beratung

Hilft dir, aus dem Linux-Programm den passenden Kurs oder Lernpfad zu finden.

Norbert Jansen

Norbert Jansen

Beratung & Inhouse

Plant mit dir Inhouse-Trainings, die exakt auf eure Systemlandschaft und Distributionen zugeschnitten sind.

Nächster Schritt

Patchprozess mit uns durchsprechen

Wenn du deinen Patchprozess von Handarbeit auf Automatisierung umstellen willst, schauen wir uns gemeinsam an, welche Systeme unbeaufsichtigt laufen können und wo du Freigaben brauchst. Melde dich für eine Kursempfehlung oder eine Inhouse-Schulung, die auf deine Distributionen und deine Betriebsrealität zugeschnitten ist.