Proxmox in Claude: Hosts per Chat verwalten mit eigenem MCP-Server
Mit einem eigenen MCP-Server (Model Context Protocol, also eine Schnittstelle, über die ein KI-Assistent Tools benutzen kann) verwaltest du deine Proxmox-Hosts direkt aus dem Chat mit Claude. So kannst du fragen, welche Container gerade laufen, wie voll deine Storages sind, oder vor einem Update einen Snapshot anlegen lassen. In diesem Guide richtest du dafür ProxmoxMCP-Plus in einem kleinen LXC ein, mit eigenem API-Token, gesperrten Shell-Befehlen und nur intern erreichbar. Bei mir verwaltet Claude so vier Proxmox-Hosts: Es listet VMs und Container, prüft Storage, liest Container-Configs und legt vor Upgrades Snapshots an. Unterwegs bin ich auf ein TLS-Problem mit Python 3.13 gestoßen, die Lösung steht in Schritt 6.
Kurz erklärt
- ProxmoxMCP-Plus ist ein Open-Source-Server, der die Proxmox-API als Tools für Claude bereitstellt: VMs und Container auflisten, Storage anzeigen, Snapshots anlegen, Gäste starten und stoppen.
- Er läuft in einem eigenen unprivilegierten LXC (ein leichter Linux-Container auf Proxmox) als systemd-Dienst (systemd startet und überwacht Dienste unter Linux) und meldet sich mit einem eigenen API-Token an, den du jederzeit widerrufen kannst.
- Shell-Befehle in deinen Gästen sind per
deny_allkomplett gesperrt. Erreichbar ist der Server nur intern über Caddy, und er verlangt selbst einen Bearer Token (ein geheimer Schlüssel im HTTP-Header). - Mit Python 3.13 lehnt der Server das Standard-Zertifikat älterer Proxmox-Installationen ab. Du löst das mit einer kleinen eigenen CA (Zertifizierungsstelle), die Zertifikatsprüfung bleibt dabei an.
- Alle Dateien aus diesem Guide (systemd-Unit, Env- und Config-Vorlage, nftables-Datei für die Firewall,
make-ca.shund kurze Anleitungen für Proxmox-User und Zertifikat) findest du im Repo mapplebox/homelab-mcp-stack im Ordnerproxmox/.
Was Claude damit kann
ProxmoxMCP-Plus bringt viele Tools mit. Die, die ich im Alltag nutze:
- Nodes, VMs und Container auflisten, mit Status, CPU und RAM (
get_nodes,get_vms,get_containers). - Storage anzeigen, also wie voll deine Pools und Laufwerke sind (
get_storage). - Configs von VMs und Containern lesen (
get_vm_config,get_container_config). - Snapshots auflisten, anlegen und zurückrollen (
list_snapshots,create_snapshot,rollback_snapshot). - Gäste starten, herunterfahren und stoppen (
start_container,shutdown_vm,stop_vmund so weiter).
Es gibt auch Tools zum Anlegen und Löschen von VMs, für Backups und für Befehle innerhalb der Gäste. Deshalb lohnt es sich, vorher festzulegen, was Claude darf. Meine Regeln:
- Ein eigener Proxmox-User mit eigenem API-Token. Wenn etwas schiefgeht, löschst du den Token, und der Zugriff ist weg. Dein root-Login bleibt davon unberührt.
- Die Command-Policy steht auf
deny_all. Claude kann damit keine Shell-Befehle in deinen VMs oder Containern ausführen. - Vor jeder Änderung kommt ein Snapshot. Das sage ich Claude auch so im Prompt.
- Der Server ist nur intern erreichbar, also aus dem LAN und über Tailscale (ein VPN, das deine Geräte in ein privates Netz verbindet). Er hängt nie öffentlich im Internet.
- Alles, was etwas kaputt machen kann (stoppen, löschen, zurückrollen), bestätige ich selbst. Claude Code fragt vor jedem Tool-Aufruf nach, solange du das Tool nicht dauerhaft erlaubt hast. Dauerhaft erlaubt habe ich nur lesende Tools.
Wie du MCP-Server generell absicherst, steht ausführlicher im Guide Claude im Homelab: MCP-Connectors sicher anbinden. Hier geht es um das konkrete Setup für Proxmox.
Was du brauchst
- Mindestens einen Proxmox-VE-Host mit root-Zugang per Shell.
- Platz für einen kleinen LXC: 1 Kern, 512 MB RAM und 4 GB Disk reichen.
- Einen Reverse Proxy (ein Server, der Anfragen annimmt und an interne Dienste weiterreicht). Ich nutze Caddy, das Zertifikate für interne Hostnamen per DNS-Challenge holt. Bei der DNS-Challenge beweist Caddy über einen kurzzeitigen DNS-Eintrag, dass dir die Domain gehört, dafür muss der Hostname nicht aus dem Internet erreichbar sein.
- Einen DNS-Eintrag in deinem LAN (Router, Pi-hole oder ähnliches), der den internen Hostnamen auf die IP von Caddy zeigen lässt.
- Claude Code auf deinem Rechner.
- OpenSSL 3 für die eigene CA. Mit
openssl versionsiehst du, was installiert ist. Unter Linux passt das meist schon. Am Mac ist von Haus aus LibreSSL installiert. OpenSSL 3 holst du dir dort mitbrew install openssl@3. Unter Windows nimmst du WSL oder Git Bash. In Git Bash setzt du vorherexport MSYS_NO_PATHCONV=1, sonst macht Git Bash aus/CN=…einen Windows-Pfad.
Im Guide nutze ich Platzhalter: Proxmox-Host 192.168.1.5 mit dem Node-Namen pve1 und dem Hostnamen pve1.example.com, Caddy auf 192.168.1.10, der MCP-LXC auf 192.168.1.25 und der interne Hostname mcp-proxmox.int.example.com. Ersetz sie überall durch deine Werte.
Schritt 1: Proxmox-User und API-Token anlegen
Leg zuerst einen eigenen User für den MCP-Server an. Das Realm pve ist die eingebaute Benutzerverwaltung von Proxmox. Der User braucht dort kein Passwort, weil er sich nur per Token anmeldet. Auf dem Proxmox-Host als root:
pveum user add mcp@pve --comment "MCP server for Claude"
Jetzt entscheidest du, wie viel Claude darf. Rollen sind in Proxmox fertige Bündel von Rechten:
PVEAuditordarf nur lesen. Claude kann damit alles anschauen, aber nichts starten, stoppen oder snapshotten. Das ist der richtige Einstieg, wenn du erst mal nur schauen willst.PVEAdmindarf fast alles, laut Proxmox-Doku aber keine Systemeinstellungen ändern und keine Rechte vergeben. Die Rolle brauchst du für Snapshots, Starten und Stoppen. Ich nutzePVEAdmin, weil ich genau diese Schreibrechte wollte.
Dann legst du den Token an. Dabei kommt es auf die Privilege Separation (Rechte-Trennung) an. Ist sie an, und das ist der Standard, hat der Token keine eigenen Rechte. Er bekommt nur das, was du ihm per ACL (Access Control List, also eine Rechte-Zuweisung auf einen Pfad) explizit gibst, und auch das nur, soweit der User selbst es darf. Du vergibst die Rolle deshalb zweimal, einmal an den User und einmal an den Token. Der Pfad / heißt: für alles im Cluster.
pveum user token add mcp@pve mcp --comment "ProxmoxMCP-Plus" --privsep 1
pveum acl modify / --roles PVEAdmin --users mcp@pve
pveum acl modify / --roles PVEAdmin --tokens 'mcp@pve!mcp'
Der Token heißt mcp, seine volle ID ist mcp@pve!mcp. Die einfachen Anführungszeichen um die ID brauchst du, weil die Bash ein ! sonst als Verweis auf frühere Befehle aus der History deuten kann. Für nur lesenden Zugriff ersetzt du beide Male PVEAdmin durch PVEAuditor.
Der erste Befehl gibt den Token-Wert aus (value), eine lange ID im UUID-Format. Proxmox zeigt ihn genau einmal an. Kopier ihn direkt in deinen Passwortmanager und füg ihn nirgends in einen Chat ein, auch nicht bei Claude. Mit pveum user token permissions mcp@pve mcp siehst du, welche Rechte der Token am Ende wirklich hat.
Das Gleiche geht in der Weboberfläche unter Datacenter → Permissions → API Tokens, dort heißt die Checkbox „Privilege Separation“. Brauchst du den Zugang nicht mehr, widerrufst du ihn mit pveum user token delete mcp@pve mcp. In einem Cluster legst du User und Token nur einmal an. Sie gelten für alle Nodes.
Schritt 2: Einen kleinen LXC anlegen
Lade zuerst das Debian-13-Template herunter. In der Proxmox-Oberfläche klickst du dafür auf deinen Storage local, dann auf CT Templates und auf Templates und wählst debian-13-standard. Auf der Shell geht es so:
pveam update
pveam available --section system | grep debian-13
pveam download local <name-aus-der-liste>
Dann legst du über Create CT einen neuen Container an: Template Debian 13, unprivilegiert (die Checkbox „Unprivileged container“ ist standardmäßig an), 1 Kern, 512 MB RAM und 4 GB Disk. Im Reiter Network trägst du bei IPv4 „Static“ mit 192.168.1.25/24 und dem Gateway deines Routers ein. Alternativ lässt du „DHCP“ stehen und reservierst die Adresse im Router, Hauptsache sie ändert sich nicht. Docker brauchst du für diesen Server nicht. Er läuft direkt als Python-Dienst. Wenn du dir das Anlegen von Containern sparen willst, schau dir die Proxmox Helper Scripts an.
Ein MCP-Server reicht für einen einzelnen Host oder für einen ganzen Cluster. Bei einem Cluster trägst du in der Config einen beliebigen Node ein, dessen API kennt alle anderen. Bei mir laufen deshalb für vier Hosts drei Instanzen: eine für den Cluster und je eine für die beiden einzelnen Hosts.
Im LXC installierst du die Grundpakete, legst einen System-User mcp ohne Login-Shell an, unter dem der Dienst später läuft, und erstellst seine Ordner mit den passenden Rechten:
apt update && apt install -y python3 curl ca-certificates nftables
useradd --system --home /var/lib/proxmox-mcp --shell /usr/sbin/nologin mcp
install -d -o mcp -g mcp -m 750 /var/lib/proxmox-mcp
install -d -o root -g mcp -m 750 /etc/proxmox-mcp
Schritt 3: ProxmoxMCP-Plus mit uv installieren
Den Server gibt es als Python-Paket proxmox-mcp-plus, der Code liegt auf GitHub bei RekklesNA/ProxmoxMCP-Plus. Ich installiere ihn mit uv, einem schnellen Paketmanager für Python, in ein eigenes venv (eine abgeschlossene Python-Umgebung) unter /opt/proxmox-mcp. So kommt nichts mit den Python-Paketen von Debian durcheinander.
curl -LsSf https://astral.sh/uv/install.sh | sh
source ~/.local/bin/env
uv venv --python /usr/bin/python3 /opt/proxmox-mcp
uv pip install --python /opt/proxmox-mcp/bin/python "proxmox-mcp-plus==0.5.27"
--python /usr/bin/python3 sorgt dafür, dass das venv das Python von Debian nutzt. Ohne die Angabe könnte uv ein eigenes Python in den Home-Ordner von root legen, und das sieht der Dienst später nicht, weil die systemd-Unit Home-Verzeichnisse ausblendet. Danach liegt das Startprogramm unter /opt/proxmox-mcp/bin/proxmox-mcp-plus.
Ich nutze Version 0.5.27 und habe sie festgenagelt, damit mich ein Update nicht überrascht. Zum Zeitpunkt des Schreibens gibt es schon neuere Versionen (zum Beispiel 0.6.2). Lies vor einem Wechsel die Release Notes im GitHub-Repo, ändere dann die Versionsnummer und führ den Befehl noch mal aus.
Schritt 4: Die config.json schreiben
Die Konfiguration liegt in /etc/proxmox-mcp/config.json. Weil dort der Token-Wert steht, legst du die Datei zuerst leer mit den richtigen Rechten an: Nur root darf schreiben, nur die Gruppe mcp darf lesen. Danach öffnest du sie im Editor:
install -m 640 -o root -g mcp /dev/null /etc/proxmox-mcp/config.json
nano /etc/proxmox-mcp/config.json
Meine Config sieht so aus, mit Platzhaltern statt echter Werte:
{
"proxmox": {
"host": "192.168.1.5",
"port": 8006,
"verify_ssl": true,
"service": "PVE"
},
"auth": {
"user": "mcp@pve",
"token_name": "mcp",
"token_value": "<token-secret-from-pveum>"
},
"logging": {
"level": "INFO"
},
"mcp": {
"host": "0.0.0.0",
"port": 8000,
"transport": "STREAMABLE"
},
"jobs": {
"sqlite_path": "/var/lib/proxmox-mcp/proxmox-jobs.sqlite3"
},
"command_policy": {
"mode": "deny_all",
"allow_patterns": [],
"deny_patterns": [
"(^|\\s)rm\\s+-rf(\\s|$)",
":\\(\\)\\{:\\|:\\&\\};:"
],
"require_approval_token": false,
"approval_token": null
}
}
Was die Blöcke bedeuten:
proxmox: Adresse und Port der Proxmox-API.verify_sslbleibt auftrue, dazu mehr in Schritt 6.auth: User, Token-Name und der Token-Wert aus Schritt 1. Den Wert trägst du selbst im Editor ein.logging: Ohne Log-Datei schreibt der Server auf die Konsole, und systemd sammelt das im Journal. Du liest es später mitjournalctl.mcp: Der Server hört auf Port 8000 auf allen IPv4-Adressen des LXC (0.0.0.0) und spricht Streamable HTTP, den aktuellen HTTP-Transport von MCP. Der Endpoint ist/mcp.jobs: Länger laufende Aufgaben merkt sich der Server in einer kleinen SQLite-Datenbank. Sie liegt im einzigen Ordner, in den der Dienst schreiben darf.command_policy:deny_allblockt jeden Befehl in Gästen, der nicht inallow_patternssteht. Die Liste ist leer, also ist alles gesperrt. Diedeny_patternssind die Vorgaben aus der Beispiel-Config des Projekts und greifen zusätzlich. Ein Freigabe-Token für Befehle (approval_token) brauchst du beideny_allnicht.
Im Code gibt es zusätzlich die Option command_policy.high_risk_mode für Operationen wie Löschen und Zurückrollen. Standard ist audit_only, dann werden solche Aufrufe nur protokolliert. Mit enforce greifen echte Sperren. Ich verlasse mich dafür auf die Rückfrage in Claude Code. Wenn du strenger sein willst, schau dir die Option in der Doku des Projekts an.
Schritt 5: Env-Datei mit Bearer-Key
Im HTTP-Modus verlangt ProxmoxMCP-Plus von sich aus einen Schlüssel in der Variable MCP_API_KEY. Jeder Request ohne Authorization: Bearer <key> bekommt eine 401. Der Key schützt nur den MCP-Server selbst und ist vom Proxmox-Token getrennt. Erzeug einen langen Zufallswert:
openssl rand -hex 32
Dann legst du die Env-Datei genauso mit den richtigen Rechten an und trägst die Werte ein:
install -m 640 -o root -g mcp /dev/null /etc/proxmox-mcp/proxmox-mcp.env
nano /etc/proxmox-mcp/proxmox-mcp.env
MCP_API_KEY=<dein-langer-zufallswert>
PROXMOX_MCP_CONFIG=/etc/proxmox-mcp/config.json
REQUESTS_CA_BUNDLE=/etc/proxmox-mcp/homelab-ca.pem
PROXMOX_MCP_CONFIG sagt dem Server, wo die Config liegt. REQUESTS_CA_BUNDLE brauchst du für Schritt 6. Denselben Key trägst du später in Claude Code ein, das ihn bei jedem Request mitschickt.
Schritt 6: Das TLS-Problem mit Python 3.13 lösen
Debian 13 bringt Python 3.13 mit. Seit dieser Version setzt Python beim Prüfen von Zertifikaten standardmäßig das Flag VERIFY_X509_STRICT, und die Library urllib3, auf der die Proxmox-Anbindung aufbaut, macht das ab Version 2.4 unter Python 3.13 ebenfalls. Strenge Prüfung heißt: Zertifikate, die nicht sauber dem Standard RFC 5280 folgen, fallen durch. Die CA, die Proxmox bei der Installation selbst erzeugt, hatte lange keine keyUsage-Erweiterung (ein Feld, das festlegt, wofür ein Schlüssel benutzt werden darf). Bei mir scheiterte deshalb jede Verbindung mit dieser Meldung:
certificate verify failed: CA cert does not include key usage extension
Prüf zuerst, ob dich das überhaupt betrifft. Auf dem Proxmox-Host:
openssl x509 -in /etc/pve/pve-root-ca.pem -noout -ext keyUsage
Steht in der Ausgabe keine Zeile mit Key Usage, fehlt die Erweiterung, und du brauchst den Rest dieses Schritts. Zum Zeitpunkt des Schreibens erzeugt Proxmox ab dem Paket pve-cluster 9.1.2 neue CAs mit keyUsage (Bug #6701). Bestehende Installationen behalten ihre alte CA. Zeigt der Befehl bei dir Certificate Sign, CRL Sign, kannst du die eigene CA überspringen. Dann kopierst du /etc/pve/pve-root-ca.pem als /etc/proxmox-mcp/homelab-ca.pem in den LXC (so wie unten beschrieben) und testest mit dem TLS-Check aus „Prüfen, ob es läuft“.
Der bequeme Weg wäre verify_ssl: false. Dann prüft der Server aber gar nicht mehr, mit wem er spricht, und das bei einem Token mit Admin-Rechten. ProxmoxMCP-Plus blockiert das laut Code sogar (auch in 0.5.27), solange du nicht zusätzlich einen Entwicklermodus einschaltest. Ich habe deshalb eine kleine eigene CA gebaut und damit für jeden Proxmox-Node ein Zertifikat ausgestellt, das alle nötigen Erweiterungen hat. Das machst du einmal auf deinem Rechner. Die Schlüssel sind ECDSA P-256, also ein modernes, kurzes Schlüsselformat, das Proxmox und Python problemlos verstehen.
Am einfachsten nimmst du make-ca.sh aus dem Repo. Du gibst Hostname, IP und einen Ausgabeordner an. Beim ersten Aufruf legt das Script die CA an, später nutzt es sie wieder und überschreibt nie einen vorhandenen Schlüssel oder ein Zertifikat. Die Dateien heißen nach dem Hostnamen:
./make-ca.sh pve1.example.com 192.168.1.5 ./pki
# erzeugt ./pki/homelab-ca.pem, ./pki/homelab-ca.key,
# ./pki/pve1.example.com.pem, ./pki/pve1.example.com.key
Wenn du lieber jeden Befehl selbst tippst, geht es auch von Hand. Zuerst die CA, 10 Jahre gültig, die nur Zertifikate signieren und keine weiteren CAs unter sich haben darf (pathlen:0):
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes \
-keyout homelab-ca.key -out homelab-ca.pem -days 3650 \
-subj "/CN=Homelab Proxmox CA" \
-addext "basicConstraints=critical,CA:TRUE,pathlen:0" \
-addext "keyUsage=critical,keyCertSign,cRLSign"
Dann pro Node eine kleine Datei mit den Erweiterungen. In die SAN-Liste (Subject Alternative Names, also alle Namen und IPs, unter denen der Host erreichbar ist) gehören die IP des Nodes, sein Hostname sowie localhost und 127.0.0.1. Speicher sie als pve1.example.com.ext:
basicConstraints = critical,CA:FALSE
keyUsage = critical,digitalSignature,keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = IP:192.168.1.5,DNS:pve1.example.com,DNS:localhost,IP:127.0.0.1
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid
Mit dieser Datei erzeugst du Schlüssel und Zertifikat für den Node. Ich stelle die Host-Zertifikate für 825 Tage aus:
openssl req -new -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes \
-keyout pve1.example.com.key -out pve1.example.com.csr -subj "/CN=pve1.example.com"
openssl x509 -req -in pve1.example.com.csr -CA homelab-ca.pem -CAkey homelab-ca.key \
-CAcreateserial -out pve1.example.com.pem -days 825 -sha256 -extfile pve1.example.com.ext
openssl verify -x509_strict -CAfile homelab-ca.pem pve1.example.com.pem
Der letzte Befehl muss pve1.example.com.pem: OK ausgeben. homelab-ca.key ist der wertvollste Teil: Wer ihn hat, kann Zertifikate ausstellen, denen deine Geräte vertrauen. Heb ihn offline oder verschlüsselt auf und kopier ihn auf keinen Server.
Jetzt installierst du das Zertifikat auf dem Node. pveproxy ist der Dienst, der die Proxmox-Weboberfläche und die API auf Port 8006 ausliefert. Für eigene Zertifikate nutzt er laut Doku die Dateien pveproxy-ssl.pem und pveproxy-ssl.key in /etc/pve/local/. Das ist ein Link auf /etc/pve/nodes/<node>/. Den Node-Namen zeigt dir hostname auf dem Node. Die von Proxmox erzeugten Dateien pve-ssl.pem und pve-root-ca.pem fasst du dabei nicht an.
scp ./pki/pve1.example.com.pem root@192.168.1.5:/etc/pve/nodes/pve1/pveproxy-ssl.pem
scp ./pki/pve1.example.com.key root@192.168.1.5:/etc/pve/nodes/pve1/pveproxy-ssl.key
ssh root@192.168.1.5 systemctl restart pveproxy
Der Neustart von pveproxy betrifft nur die Weboberfläche und die API. Deine VMs und Container laufen weiter. In einem Cluster wiederholst du das für jeden Node mit seinem eigenen Zertifikat und derselben CA. Willst du zurück zum Proxmox-Zertifikat, führst du auf dem Node pvenode cert delete 1 aus. Das entfernt die beiden pveproxy-ssl-Dateien und startet pveproxy neu.
Ins LXC kommt nur das öffentliche CA-Zertifikat homelab-ca.pem. Kopier es auf den Proxmox-Host und schieb es von dort mit pct push in den Container. <ct-id> ist die ID deines MCP-LXC:
scp ./pki/homelab-ca.pem root@192.168.1.5:/root/homelab-ca.pem
ssh root@192.168.1.5 pct push <ct-id> /root/homelab-ca.pem /etc/proxmox-mcp/homelab-ca.pem --perms 644
Alternativ öffnest du homelab-ca.pem auf deinem Rechner, kopierst den Text von -----BEGIN CERTIFICATE----- bis -----END CERTIFICATE----- und fügst ihn im LXC mit nano /etc/proxmox-mcp/homelab-ca.pem ein. Über REQUESTS_CA_BUNDLE aus Schritt 5 vertraut der Server genau dieser CA. Importierst du homelab-ca.pem auch in deinen Browser oder dein Betriebssystem, verschwindet außerdem die Zertifikatswarnung in der Proxmox-Oberfläche.
Schritt 7: Firewall mit nftables, Port 8000 nur für Caddy
Bevor du den Dienst startest, sperrst du seinen Port. Der Server hört auf allen IPv4-Adressen des LXC. Damit niemand im LAN an Caddy vorbei direkt auf Port 8000 geht, lässt du dort nur die IP von Caddy durch. Debian 13 nutzt dafür nftables, den Nachfolger von iptables im Linux-Kernel. Ersetz den Inhalt von /etc/nftables.conf im LXC durch diese Datei und trag bei CADDY_IP die IP deines Caddy ein:
#!/usr/sbin/nft -f
flush ruleset
define CADDY_IP = 192.168.1.10 # your Caddy host
define MCP_PORT = 8000 # ProxmoxMCP-Plus
table inet filter {
chain input {
type filter hook input priority filter; policy accept;
iifname "lo" accept
tcp dport $MCP_PORT ip saddr $CADDY_IP accept
tcp dport $MCP_PORT drop # everyone else, IPv4 and IPv6
}
chain forward {
type filter hook forward priority filter; policy accept;
}
chain output {
type filter hook output priority filter; policy accept;
}
}
Die Regeln beschränken nur Port 8000. Alles andere, auch SSH, bleibt offen, du kannst dich mit dieser Datei also nicht aussperren. flush ruleset ersetzt nur die Regeln im LXC, weil der Container sein eigenes Netzwerk hat. Der Proxmox-Host ist davon nicht betroffen. Die Tabelle inet gilt für IPv4 und IPv6 gleichzeitig, die letzte Regel blockt Port 8000 also auch über IPv6. Loopback (lo) bleibt erlaubt, damit du im Container selbst mit localhost testen kannst.
nft -c -f /etc/nftables.conf
systemctl enable --now nftables
nft list ruleset
nft -c prüft die Datei nur und ändert nichts. Bei einem Tippfehler zeigt es die Zeile an. systemctl enable --now nftables lädt die Regeln sofort und bei jedem Boot. Änderst du die Datei später, lädst du sie mit nft -f /etc/nftables.conf neu.
Schritt 8: Gehärtete systemd-Unit
systemd ist der Dienst-Manager von Debian. Er startet den Server beim Booten und nach einem Absturz neu. Die Unit unten ist meine echte. Die Zeilen ab NoNewPrivileges sperren den Dienst ein: Er kann sich keine weiteren Rechte holen, sieht das Dateisystem nur lesend, sieht keine Home-Ordner und schreibt nur nach /var/lib/proxmox-mcp. Speicher sie als /etc/systemd/system/proxmox-mcp.service:
[Unit]
Description=ProxmoxMCP-Plus HTTP server
After=network-online.target
Wants=network-online.target
[Service]
User=mcp
Group=mcp
EnvironmentFile=/etc/proxmox-mcp/proxmox-mcp.env
WorkingDirectory=/var/lib/proxmox-mcp
ExecStart=/opt/proxmox-mcp/bin/proxmox-mcp-plus
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/proxmox-mcp
PrivateTmp=true
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable --now proxmox-mcp
journalctl -u proxmox-mcp -f
Schritt 9: Caddy mit int_only
Leg zuerst in deinem LAN-DNS einen Eintrag an, der mcp-proxmox.int.example.com auf die IP von Caddy (192.168.1.10) zeigen lässt. Im Caddyfile nutze ich für alle internen MCP-Server ein gemeinsames Snippet (ein wiederverwendbarer Baustein). Es holt das Zertifikat per DNS-Challenge und lehnt alles ab, was nicht aus privaten Netzen oder dem Tailscale-Bereich 100.64.0.0/10 kommt. Im Repo steht als Beispiel IONOS als DNS-Provider. Dafür brauchst du einen Caddy-Build mit dem Modul caddy-dns/ionos und den API-Token in der Variable IONOS_API_TOKEN. Bei einem anderen Provider tauschst du die dns-Zeile und das Modul aus.
(int_only) {
tls {
dns ionos {env.IONOS_API_TOKEN}
}
@outside not remote_ip private_ranges 100.64.0.0/10
respond @outside 403
}
mcp-proxmox.int.example.com {
import int_only
reverse_proxy 192.168.1.25:8000
}
Den Bearer Token prüft ProxmoxMCP-Plus selbst über MCP_API_KEY. Caddy reicht den Authorization-Header einfach durch, im Caddyfile steht dadurch kein Secret für diesen Server. Danach systemctl reload caddy.
Schritt 10: Den Server in Claude Code eintragen
Auf deinem Rechner fügst du den Server in Claude Code hinzu. Mit --scope user steht er dir in allen Projekten zur Verfügung:
claude mcp add --transport http --scope user proxmox \
https://mcp-proxmox.int.example.com/mcp \
--header "Authorization: Bearer <dein-langer-zufallswert>"
Das Leerzeichen vor claude ist Absicht. In vielen Bash-Setups (mit HISTCONTROL=ignorespace oder ignoreboth, bei Debian und Ubuntu der Standard) landet ein Befehl mit führendem Leerzeichen nicht in der History, und damit auch der Key nicht. In der zsh brauchst du dafür setopt HIST_IGNORE_SPACE. Gespeichert wird der Key trotzdem im Klartext in ~/.claude.json, behandel die Datei also wie ein Passwort.
Hast du mehrere Hosts mit eigenen Instanzen, gibst du jedem einen eigenen Namen, zum Beispiel proxmox-pve1 und proxmox-pve2. Dann weiß Claude, welchen Host du meinst.
Erste Prompts zum Ausprobieren
Starte eine neue Session in Claude Code und probier zum Beispiel diese Prompts. Fang mit lesenden an:
- „Zeig mir alle VMs und Container mit Status, CPU und RAM.“
- „Wie voll sind meine Storages? Gibt es etwas, das knapp wird?“
- „Zeig mir die Config von CT 120 und erklär mir, was die Optionen bedeuten.“
- „Ich will CT 120 updaten. Leg vorher einen Snapshot mit dem Namen pre-upgrade an und zeig mir danach die Snapshot-Liste.“
Beim Snapshot fragt Claude Code nach, bevor es das Tool aufruft. Lies dir die Parameter an der Stelle kurz durch, vor allem die ID des Gasts. So halte ich es auch beim Stoppen und Neustarten.
Prüfen, ob es läuft
- Im LXC:
systemctl status proxmox-mcpzeigt „active (running)“. Die Logs siehst du mitjournalctl -u proxmox-mcp -f. - Der Node liefert dein Zertifikat aus:
openssl s_client -connect 192.168.1.5:8006 -CAfile ./pki/homelab-ca.pem -verify_return_error </dev/null 2>/dev/null | grep "Verify return code"auf deinem Rechner zeigtVerify return code: 0 (ok). - Python im LXC akzeptiert das Zertifikat, wenn dieser Befehl
401ausgibt (die API will eine Anmeldung, das Zertifikat wurde aber akzeptiert):REQUESTS_CA_BUNDLE=/etc/proxmox-mcp/homelab-ca.pem /opt/proxmox-mcp/bin/python -c "import requests; print(requests.get('https://192.168.1.5:8006/api2/json/version').status_code)". Kommt stattdessen einSSLError, stimmt etwas mit dem Zertifikat nicht. - Der Server verlangt den Key:
curl -i http://127.0.0.1:8000/mcpim LXC liefert 401. - Die Firewall greift: Von einem anderen Rechner im LAN (nicht vom Caddy-Host) läuft
curl -m 5 http://192.168.1.25:8000/mcpin einen Timeout. - Der Weg über Caddy klappt:
curl -i https://mcp-proxmox.int.example.com/mcpohne Header gibt 401 vom Server, aus einem fremden Netz 403 von Caddy. claude mcp listzeigt den Server als verbunden. In einer laufenden Session siehst du mit/mcpden Status und die Tools.
Häufige Probleme
„CA cert does not include key usage extension“
Das ist das Python-3.13-Problem aus Schritt 6. Prüf, ob REQUESTS_CA_BUNDLE in der Env-Datei steht, ob homelab-ca.pem für den User mcp lesbar ist und ob pveproxy nach dem Kopieren neu gestartet wurde. Mit dem openssl s_client-Check von oben siehst du, welches Zertifikat der Node gerade ausliefert.
Hostname mismatch
Der Wert bei proxmox.host muss in der SAN-Liste des Zertifikats stehen. Trägst du eine IP ein, braucht das Zertifikat IP: mit genau dieser Adresse, bei einem Hostnamen den passenden DNS:-Eintrag.
Der Dienst startet nicht
Schau in journalctl -u proxmox-mcp. Fehlt MCP_API_KEY, bricht der Server im HTTP-Modus ab. Der Key darf keine Leerzeichen enthalten. Ein Hex-Wert aus openssl rand passt. Meldet der Log fehlende Schreibrechte, liegt ein Pfad aus der Config außerhalb von /var/lib/proxmox-mcp.
Claude sieht nichts oder bekommt 403 von Proxmox
Meist fehlt die ACL für den Token. Mit Privilege Separation braucht der Token seine eigene Rolle, die Rolle am User allein reicht nicht. pveum user token permissions mcp@pve mcp zeigt, was wirklich ankommt.
Claude Code zeigt „Failed to connect“
claude mcp get proxmox zeigt dir Status und Konfiguration des Servers. Ein 401 heißt, dass der Key in Claude Code nicht mit MCP_API_KEY übereinstimmt. Ein 502 von Caddy heißt meist, dass in der nftables-Datei eine andere IP als die von Caddy steht oder der Dienst nicht läuft.
Fazit
Mit diesem Setup fragst du Claude nach dem Zustand deiner Proxmox-Hosts wie einen Kollegen, der Zugriff auf die Oberfläche hat. Der Zugriff hängt an einem Token, den du mit einem Befehl widerrufst. Shell-Befehle in Gästen sind gesperrt, und von außen kommt niemand an den Server. Das TLS-Problem mit Python 3.13 hat mich am meisten Zeit gekostet. Mit der eigenen CA ist es sauber gelöst, und die Zertifikatswarnung im Browser bin ich dabei auch losgeworden. Wenn du unsicher bist, fang mit PVEAuditor an und gib Schreibrechte erst, wenn du dem Ablauf vertraust. Alle Dateien findest du im Repo mapplebox/homelab-mcp-stack.
Nutzt du Claude schon für dein Homelab? Schreib in die Kommentare, welche Aufgaben du ihm gibst.
Hat dir der Guide geholfen? Dann spendier mir gern einen Kaffee auf Ko-fi. Damit bleibt MappleBox werbefrei.