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:
- Ermittlung des Proxmox-Node-Namens.
- Erfassung aller laufenden virtuellen Maschinen und LXC-Container.
- Kontrolliertes Herunterfahren aller laufenden Gäste.
- Aktualisierung der Paketquellen und Installation der Updates.
- Prüfung, ob ein Neustart erforderlich ist.
- Neustart des Proxmox-Hosts bei Bedarf.
- Überprüfung der Erreichbarkeit der Proxmox-API.
- 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 jsonDie 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 jsonFür LXC-Container entsprechend:
pvesh get /nodes/<NODENAME>/lxc --output-format jsonDie 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 300Ich 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: 0Anschließend installiere ich die verfügbaren Updates:
- name: Upgrade Proxmox VE packages
ansible.builtin.apt:
upgrade: distIch 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-requiredExistiert 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_requiredIst 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.existsDabei 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 jsonDas 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.deAnschließend startest du die Automatisierung mit:
ansible-playbook -i inventory.ini proxmox-update.ymlAnsible ü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.

