Proxmox-Updates? Das erledigt jetzt Ansible für mich!

Wer Proxmox VE im Homelab betreibt, kennt das Thema: Regelmäßig erscheinen Updates für den Hypervisor, Sicherheitslücken werden geschlossen und neue Funktionen bereitgestellt. Doch gerade, wenn auf dem Proxmox-Host mehrere virtuelle Maschinen und LXC-Container laufen, möchte man nicht manuell ein apt dist-upgrade ausführen und anschließend den Server neu starten.

Eine Frage, die ich in diesem Zusammenhang häufiger von meinen Lesern bekommen habe, lautet: Wie lassen sich Proxmox-Updates automatisieren, ohne dass virtuelle Maschinen und Container manuell heruntergefahren und wieder gestartet werden müssen? In meinem Homelab habe ich genau diesen Prozess mit Ansible automatisiert.

Dabei übernimmt ein Playbook die komplette Update-Prozedur: Es ermittelt zunächst alle laufenden VMs und LXC-Container, fährt diese kontrolliert herunter, installiert die verfügbaren Updates und startet den Proxmox-Host bei Bedarf neu. Anschließend werden die zuvor laufenden Gäste wieder gestartet.

In diesem Artikel zeige ich dir, wie ich das umgesetzt habe und wie du dir eine solche Automatisierung für dein eigenes Homelab aufbauen kannst. Grundlegende Kenntnisse in Ansible sind von Vorteil, um das Playbook und seine Funktionsweise besser nachvollziehen zu können. Allerdings ist das kein Muss: Auch ohne Vorkenntnisse lässt sich das Ganze in Betrieb nehmen.

1. Voraussetzungen & Vorbereitung:

Für die Automatisierung verwende ich Ansible. Das hat den Vorteil, dass ich keine zusätzliche Software auf dem Proxmox-Host installieren muss. Lediglich eine funktionierende SSH-Verbindung vom Ansible-Control-Node zum Proxmox-Server wird benötigt.

Mein Setup besteht aus folgenden Komponenten:

  • Ein einzelner Proxmox VE Hypervisor
  • Ein separater Ansible-Control-Node
  • SSH-Zugriff als root
  • Ein Ansible-Inventar mit der Hostgruppe Proxmox

Das Playbook durchläuft dabei mehrere Schritte:

  1. Ermittlung des Proxmox-Node-Namens.
  2. Erfassung aller laufenden virtuellen Maschinen und LXC-Container.
  3. Kontrolliertes Herunterfahren aller laufenden Gäste.
  4. Aktualisierung der Paketquellen und Installation der Updates.
  5. Prüfung, ob ein Neustart erforderlich ist.
  6. Neustart des Proxmox-Hosts bei Bedarf.
  7. Überprüfung der Erreichbarkeit der Proxmox-API.
  8. Wiederherstellen des ursprünglichen Betriebszustands durch Starten der zuvor laufenden Gäste.

💡 Gut zu wissen:

Es werden ausschließlich VMs und Container wieder gestartet, die vor Beginn des Update-Prozesses tatsächlich gelaufen sind. Manuell gestoppte Gäste bleiben somit auch nach dem Update gestoppt.

2. Das Ansible Playbook:

Im weiteren Verlauf des Artikels gehe ich Schritt für Schritt auf die einzelnen Tasks ein und erläutere deren Funktion sowie das Zusammenspiel der verschiedenen Komponenten. Außerdem zeige ich dir, wie du das Playbook für dein eigenes Homelab vorbereitest und in Betrieb nimmst. Das vollständige Ansible-Playbook findest du bereits vorab in der folgenden Codebox:

---
- name: Proxmox VE Single-Node Updates
  hosts: Proxmox
  gather_facts: false

  vars:
    # Timeout für das Herunterfahren von VMs und Containern
    guest_shutdown_timeout: 300

    # Timeout für den Reboot
    pve_reboot_timeout: 900

  tasks:

    #----------------------------------------------------------
    # PVE Node Namen nach PVE-Schema erfassen
    #----------------------------------------------------------

    - name: Get PVE node name
      ansible.builtin.command:
        cmd: pvesh get /nodes --output-format json
      register: pve_nodes
      changed_when: false

    - name: Set PVE node name
      ansible.builtin.set_fact:
        pve_node: "{{ (pve_nodes.stdout | from_json | first).node }}"

    # ---------------------------------------------------------
    # VMs & LXC ermitteln
    # ---------------------------------------------------------

    - name: Get running VMs
      ansible.builtin.command:
        cmd: "pvesh get /nodes/{{ pve_node }}/qemu --output-format json"
      register: vm_list
      changed_when: false

    - name: Build list of running VMIDs
      ansible.builtin.set_fact:
        running_vm_list: >-
          {{
            vm_list.stdout | from_json
            | selectattr('status', 'equalto', 'running')
            | map(attribute='vmid')
            | list
          }}

    - name: Get running containers
      ansible.builtin.command:
        cmd: "pvesh get /nodes/{{ pve_node }}/lxc --output-format json"
      register: ct_list
      changed_when: false

    - name: Build list of running container IDs
      ansible.builtin.set_fact:
        running_ct_list: >-
          {{
            ct_list.stdout | from_json
            | selectattr('status', 'equalto', 'running')
            | map(attribute='vmid')
            | list
          }}

    # ---------------------------------------------------------
    # VMs & LXC herunterfahren
    # ---------------------------------------------------------

    - name: Shutdown VMs
      ansible.builtin.command:
        cmd: "qm shutdown {{ item }} --timeout {{ guest_shutdown_timeout }}"
      loop: "{{ running_vm_list }}"

    - name: Shutdown containers
      ansible.builtin.command:
        cmd: "pct shutdown {{ item }} --timeout {{ guest_shutdown_timeout }}"
      loop: "{{ running_ct_list }}"

    - name: Wait until all VMs are stopped
      ansible.builtin.command:
        cmd: "qm status {{ item }}"
      register: vm_status
      changed_when: false
      until: "'status: stopped' in vm_status.stdout"
      retries: 60
      delay: 5
      loop: "{{ running_vm_list }}"

    - name: Wait until all containers are stopped
      ansible.builtin.command:
        cmd: "pct status {{ item }}"
      register: ct_status
      changed_when: false
      until: "'status: stopped' in ct_status.stdout"
      retries: 60
      delay: 5
      loop: "{{ running_ct_list }}"

    # ---------------------------------------------------------
    # Proxmox VE aktualisieren
    # ---------------------------------------------------------

    - name: Update package cache
      ansible.builtin.apt:
        update_cache: true
        cache_valid_time: 0

    - name: Upgrade Proxmox VE packages
      ansible.builtin.apt:
        upgrade: dist

    # ---------------------------------------------------------
    # Proxmox VE rebooten
    # ---------------------------------------------------------

    - name: Check if reboot is required
      ansible.builtin.stat:
        path: /var/run/reboot-required
      register: reboot_required

    - name: Reboot Proxmox node if required
      ansible.builtin.reboot:
        reboot_timeout: "{{ pve_reboot_timeout }}"
        connect_timeout: 10
        post_reboot_delay: 30
      when: reboot_required.stat.exists

    - name: Wait for Proxmox API
      ansible.builtin.command:
        cmd: pvesh get /version --output-format json
      register: pve_api
      changed_when: false
      until: pve_api.rc == 0
      retries: 30
      delay: 10

    # ---------------------------------------------------------
    # VMs & LXCs wieder starten
    # ---------------------------------------------------------

    - name: Start VMs
      ansible.builtin.command:
        cmd: "qm start {{ item }}"
      loop: "{{ running_vm_list }}"

    - name: Start containers
      ansible.builtin.command:
        cmd: "pct start {{ item }}"
      loop: "{{ running_ct_list }}"

3. Laufende Gäste erkennen:

Bevor ich mit den Updates beginne, muss ich zunächst wissen, welche virtuellen Maschinen und Container überhaupt laufen.Dafür nutze ich die Proxmox-API über das Kommandozeilenwerkzeug pvesh.

Zunächst ermittle ich den Namen des Proxmox-Nodes:

pvesh get /nodes --output-format json

Die Ausgabe wird von Ansible als JSON verarbeitet. Anschließend verwende ich den ermittelten Node-Namen, um die virtuellen Maschinen und LXC-Container abzufragen.

Für virtuelle Maschinen verwende ich:

pvesh get /nodes/<NODENAME>/qemu --output-format json

Für LXC-Container entsprechend:

pvesh get /nodes/<NODENAME>/lxc --output-format json

Die Rückgabe enthält unter anderem den Status der jeweiligen Gäste. Mithilfe der Ansible-Filter selectattr und map filtere ich ausschließlich die Einträge mit dem Status running heraus und speichere deren VMIDs beziehungsweise Container-IDs in Variablen. Damit habe ich später genau die Gäste zur Verfügung, die vor dem Update heruntergefahren werden müssen.

4. VMs und LXCs herunterfahren:

Im nächsten Schritt fahre ich alle zuvor ermittelten Gäste herunter. Hierfür verwende ich die Proxmox-Kommandos qm shutdown und pct shutdown.

qm shutdown <VMID> --timeout 300
pct shutdown <CTID> --timeout 300

Ich verwende bewusst ein reguläres Shutdown-Kommando anstelle eines harten Stopps. Dadurch erhalten die Betriebssysteme innerhalb der VMs und Container die Möglichkeit, ihre laufenden Prozesse sauber zu beenden.

Über den Parameter –timeout definiere ich eine maximale Wartezeit von 300 Sekunden. Anschließend prüft Ansible über qm status und pct status, ob tatsächlich alle Gäste heruntergefahren wurden. Dafür verwende ich die Kombination aus until, retries und delay.

Ansible prüft den Status bis zu 60-mal und wartet zwischen den einzelnen Versuchen jeweils fünf Sekunden. Das ergibt eine maximale zusätzliche Wartezeit von fünf Minuten pro Gast. Erst wenn alle zuvor laufenden VMs und Container gestoppt sind, geht es mit den eigentlichen Updates weiter.

5. Proxmox-Updates installieren:

Für die Installation der Updates verwende ich das Ansible-Modul apt. Zunächst aktualisiere ich den lokalen Paketindex:

- name: Update package cache
  ansible.builtin.apt:
    update_cache: true
    cache_valid_time: 0

Anschließend installiere ich die verfügbaren Updates:

- name: Upgrade Proxmox VE packages
  ansible.builtin.apt:
    upgrade: dist

Ich verwende hier bewusst dist als Upgrade-Modus. Dadurch können bei Bedarf auch Paketabhängigkeiten angepasst, zusätzliche Pakete installiert oder nicht mehr benötigte Pakete entfernt werden. Gerade bei Proxmox VE ist das wichtig, da Updates häufig mehrere voneinander abhängige Komponenten betreffen.

💡 Hinweis:

Die beiden Tasks lassen sich auch problemlos in einem einzigen Task zusammenfassen. Das Ansible-Modul apt unterstützt sowohl die Aktualisierung des Paketindexes (update_cache) als auch das anschließende Distributionsupgrade (upgrade: dist). Ich habe mich hier bewusst für zwei separate Tasks entschieden, um die einzelnen Schritte im Artikel besser erklären zu können.

6. Proxmox bei Bedarf neustarten:

Nach der Installation der Updates prüfe ich, ob ein Neustart des Systems erforderlich ist. Dafür verwende ich das von Debian bereitgestellte Flag:

/var/run/reboot-required

Existiert diese Datei, signalisiert sie, dass ein Neustart erforderlich ist. Die Prüfung erfolgt über das Ansible-Modul stat:

- name: Check if reboot is required
  ansible.builtin.stat:
    path: /var/run/reboot-required
  register: reboot_required

Ist die Datei vorhanden, führt Ansible einen Neustart des Proxmox-Hosts durch:

- name: Reboot Proxmox node if required
  ansible.builtin.reboot:
    reboot_timeout: 900
    connect_timeout: 10
    post_reboot_delay: 30
  when: reboot_required.stat.exists

Dabei wartet Ansible bis zu 900 Sekunden auf den erfolgreichen Neustart und die erneute Erreichbarkeit des Systems. Anschließend prüfe ich zusätzlich die Proxmox-API:

pvesh get /version --output-format json

Das ist sinnvoll, da ein erfolgreicher SSH-Verbindungsaufbau nicht zwangsläufig bedeutet, dass auch sämtliche Proxmox-Dienste wieder vollständig zur Verfügung stehen. Mit until, retries und delay wartet Ansible deshalb auf eine erfolgreiche Antwort der API.

Alternativ zur schlanken Lösung mit dem Stat-Modul kannst du auch das CLI-Tool needrestart verwenden, um nach der Installation von Updates zu prüfen, ob ein Neustart des Proxmox-Hosts erforderlich ist:

- name: Check if reboot is required
  ansible.builtin.command:
    cmd: needrestart -k
  register: needrestart_result
  changed_when: false
  failed_when: false

- name: Reboot Proxmox node if required
  ansible.builtin.reboot:
    reboot_timeout: "{{ pve_reboot_timeout }}"
    connect_timeout: 10
    post_reboot_delay: 30
  when: "'NEEDRESTART-KSTA: 2' in needrestart_result.stdout"

💡 Gut zu wissen:

Mit needrestart kannst du auch unabhängig von Ansible prüfen, ob nach der Installation von Updates noch ein Neustart des Systems erforderlich ist. Das Tool ist unter Proxmox VE allerdings nicht standardmäßig installiert und muss zunächst mit apt install needrestart nachinstalliert werden.

7. VMs und LXCs wieder booten:

Nachdem der Proxmox-Host wieder erreichbar ist und die API funktioniert, starte ich die zuvor ermittelten Gäste wieder. Dafür verwende ich:

qm start <VMID>
pct start <CTID>

Ansible durchläuft dafür die beiden zuvor erstellten Listen mit den VMIDs und Container-IDs. Da ich die ursprünglichen Zustände vor dem Update gespeichert habe, werden ausschließlich die Gäste gestartet, die auch vorher gelaufen sind. Manuell gestoppte VMs und Container bleiben somit unangetastet. Damit ist der komplette Update-Prozess abgeschlossen.

8. Die Automatisierung starten:

Das Playbook kannst du jetzt beispielsweise unter proxmox-update.yml speichern. In deinem Ansible-Inventar-File inventory.ini muss der PVE-Host der Gruppe Proxmox zugeordnet sein:

[Proxmox]
pve-01.fabianscloud.de

Anschließend startest du die Automatisierung mit:

ansible-playbook -i inventory.ini proxmox-update.yml

Ansible übernimmt nun sämtliche beschriebenen Schritte vollständig selbstständig. Wenn du möchtest, kannst du die Ausführung zusätzlich über einen Cronjob oder einen systemd-Timer regelmäßig einplanen. In Kombination mit einem definierten Downtime-Fenster in deinem Monitoring kannst du dich anschließend entspannt zurücklehnen. Nur im Fehlerfall musst du noch manuell eingreifen.

💡 Gut zu wissen:

Du kannst auch die Monitoring-Downtime automatisiert über Ansible erstellen und nach Abschluss der Wartungsarbeiten wieder aufheben. Das lässt sich bei vielen Monitoring-Lösungen über einen einfachen API-Aufruf realisieren. Entweder steht dir dafür bereits ein passendes Ansible-Modul zur Verfügung oder du setzt die Integration über das URI-Modul und einen API-Aufruf um.

9. Single-Node war erst der Anfang:

Mit Ansible lassen sich auch in einem kleinen Homelab wiederkehrende Wartungsaufgaben sehr komfortabel automatisieren. Gerade bei einem einzelnen Proxmox-Host ist das beschriebene Playbook eine praktische Lösung, um den Update-Prozess reproduzierbar und ohne manuelle Eingriffe durchzuführen.

Ich muss mich nicht mehr darum kümmern, welche VMs und Container gerade laufen, diese einzeln herunterfahren, die Updates installieren und anschließend alles wieder starten. Das übernimmt Ansible für mich. Das Playbook lässt sich mit wenigen Anpassungen auch für ein Setup mit zwei oder mehr Proxmox-Hosts verwenden – unabhängig davon, ob diese miteinander geclustert sind oder nicht.

Dafür kannst du beispielsweise die Angabe serial: 1 im Playbook ergänzen. Damit stellt Ansible sicher, dass die Proxmox-Hosts nacheinander und nicht parallel aktualisiert werden. Das ist besonders sinnvoll, wenn du Dienste redundant auf mehreren Hosts betreibst. Ein typisches Beispiel wären zwei DNS-Server im Failover-Betrieb.

Durch die sequenzielle Aktualisierung bleibt jeweils ein DNS-Server verfügbar, während der andere gewartet und anschließend wieder in Betrieb genommen wird. Betreibst du beide Server mit einer gemeinsamen virtuellen IP (VIP), beispielsweise mit keepalived, ist das eine besonders elegante Lösung für einen unterbrechungsarmen Homelabbetrieb.

⚠️ Wichtiger Hinweis für den Einsatz in produktiven Umgebungen:

Das Playbook ist bewusst auf mein Single-Node-Homelab ausgelegt. Bei mehreren Proxmox-Nodes würde ich die Update-Strategie entsprechend erweitern und beispielsweise Wartungszustände, HA-Ressourcen und die Verfügbarkeit der Workloads berücksichtigen.

Von Fabian Wüst

Er ist leidenschaftlicher Open-Source-Benutzer und ein begeisterter Technologie-Enthusiast. Als kreativer Kopf hinter Homelabtopia bringt Fabian hier seine umfangreiche Erfahrung als Linux-Admin ein. Um sicherzustellen, dass du aus seinen Beiträgen den größtmöglichen Nutzen ziehen kannst, führt er ausgiebige Tests durch und errichtet dafür immense Setups.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert