# unraid-podman — Architekturplanung Natives Unraid-Plugin zur vollständigen Integration von Podman (rootful, Phase 1) als gleichberechtigte Alternative zu Docker, mit dem langfristigen Ziel, Docker optional abschaltbar zu machen. Status: Planungsdokument, kein Code. Dient als Grundlage für Implementierungs-Tickets. --- ## 0. Ziele & Nicht-Ziele **Ziele (Phase 1 / MVP):** - Podman rootful lauffähig unter Unraid, überlebt Reboots (Config, Images, Container, Volumes, Netzwerke). - Koexistenz mit Docker ohne Konflikte (Storage, Netzwerk, Ports, iptables-Ketten). - Start/Stop/Autostart analog zum bestehenden `rc.docker`-Muster, ohne systemd. - Sauberer Update-/Rollback-Pfad für das Plugin selbst. - Grundlegendes Logging & Fehlerdiagnose, das GUI-Meldungen von Unraid nutzt. - Vorbereitung (nicht Umsetzung) einer WebUI-Anbindung. **Nicht-Ziele (bewusst verschoben):** - Rootless Podman (Phase 2+, andere Storage-/UID-Mapping-Anforderungen). - Podman Pods / Kubernetes-YAML-Support (Phase 3, siehe Roadmap). - 1:1-Ersatz der Community-Applications-Vorlagen (erfordert eigenen Übersetzungslayer, separat geplant). - Abschalten von Docker (erst wenn Podman-Pfad stabil ist, siehe Abschnitt 17). --- ## 1. Rahmenbedingungen von Unraid, die die Architektur erzwingen | Eigenschaft | Konsequenz für das Plugin | |---|---| | Unraid läuft komplett im RAM (SquashFS + tmpfs-Overlay via `unionfs`) | Alles, was `/`, `/etc`, `/usr`, `/var` betrifft, ist bei jedem Reboot verloren. Nichts darf implizit dort persistiert werden. | | Persistenter Speicher nur unter `/boot` (USB-Stick, FAT32, klein, langsam, schreibintensiv riskant) und `/mnt/*` (Array/Cache/Pools) | Konfiguration klein & textbasiert auf `/boot/config/plugins/...`; große/volatile Daten (Images, Container-Storage, Logs) auf Array/Cache. | | Kein systemd, sondern BSD-artige `/etc/rc.d/rc.*`-Skripte, angestoßen über `/boot/config/go` | Eigenes `rc.podman`-Init-Skript im klassischen `start|stop|restart|status`-Stil, keine `.service`-Units, keine `systemctl`-Aufrufe irgendwo im Code. | | Slackware-Basis, Pakete als `.txz` (pkgtools: `installpkg`/`upgradepkg`/`removepkg`) | Podman + Abhängigkeiten müssen als Slackware-kompatible `.txz` gebaut werden, nicht als RPM/DEB. | | Plugins werden über `.plg`-XML installiert (lädt Pakete, führt ``-Blöcke mit Pre-/Post-Scripts aus) | Das `.plg` ist der zentrale Installations-/Update-/Deinstallations-Mechanismus, vergleichbar mit einem deklarativen Installer. | | `/mnt/user` (shfs/FUSE) hat Einschränkungen für Overlay-Filesysteme (fehlendes `d_type` in manchen Konstellationen, FUSE-Overhead) | Podman-Storage (`overlay`-Driver) nicht direkt auf `/mnt/user/...` legen, sondern auf ein dediziertes Loopback-Image oder einen direkt gemounteten Pool/Disk-Pfad (Analogie zu `docker.img` bzw. Docker-„Directory“-Modus seit 6.12). | | Array kann offline sein / erst spät im Boot verfügbar (`emhttpd`, Array-Start ist ein User-Trigger, kein automatischer Boot-Schritt) | `rc.podman` darf nicht blind beim Boot starten, sondern muss auf Array-/Pool-Verfügbarkeit warten bzw. vom `dynamix`-Event „array started“ getriggert werden, wie es `rc.docker` heute schon tut. | | Unraid pflegt eigene Firewall-/NAT-Logik (`iptables`) für Docker-Netzwerke (`docker0`, custom bridges, macvlan) | Podman-Netzwerke (netavark) müssen eigene, klar abgegrenzte iptables-Ketten/Chains verwenden, um nicht mit Dockers Regeln zu kollidieren. | --- ## 2. Gesamtarchitektur (Überblick) ``` ┌───────────────────────────────────────────┐ │ /boot (USB, FAT32) │ │ config/plugins/podman/ │ │ ├─ podman.cfg (Settings) │ │ ├─ autostart (Flat-File) │ │ ├─ network.json │ │ ├─ containers.conf / storage.conf (Vorlagen) │ └─ backup/ (vorherige Paketversionen) │ └───────────────────┬─────────────────────────┘ │ beim Boot kopiert nach /etc, /var/lib ▼ ┌────────────────────────────────────────────────────────────────────┐ │ RAM-Root (tmpfs/unionfs) │ │ /etc/rc.d/rc.podman (Init-Skript) │ │ /etc/containers/* (aus /boot kopiert) │ │ /usr/bin/podman, conmon, crun, netavark, aardvark-dns, ... │ │ /usr/local/emhttp/plugins/podman/ (WebUI-Stub, PHP) │ └───────────────────────────┬───────────────────────────────────────┘ │ bind-mount / Storage-Root zeigt auf ▼ ┌────────────────────────────────────────────────────────────────────┐ │ Persistenter Bereich (Cache-Pool empfohlen) │ │ /mnt/cache/system/podman/ │ │ ├─ podman.img (Loopback, XFS/BTRFS, für overlay-Storage) │ │ │ → gemountet auf /var/lib/containers/storage │ │ ├─ logs/ │ │ └─ networks/ (netavark state, falls nicht im Image) │ └────────────────────────────────────────────────────────────────────┘ ``` Kernprinzip: **Trennung von „Konfiguration“ (klein, Text, `/boot`) und „Nutzdaten“ (groß, binär, Array/Cache)** — exakt das bestehende Unraid-Muster, das auch `dynamix.docker.manager` verwendet. --- ## 3. Pluginstruktur ### 3.1 Repository-Layout (Build-/Source-Repo, nicht das, was auf Unraid landet) ``` unraid-podman/ ├── podman.plg # Haupt-Plugin-Manifest (XML) ├── packages/ # Slackware .txz Build-Rezepte │ ├── podman/ │ ├── conmon/ │ ├── crun/ │ ├── netavark/ │ ├── aardvark-dns/ │ ├── containers-common/ │ ├── fuse-overlayfs/ │ └── passt/ ├── source/ │ ├── rc.podman # Init-Skript │ ├── podman-preflight.sh # Startup-Checks │ ├── podman-autostart.sh │ ├── podman-backup.sh │ └── config-templates/ │ ├── containers.conf │ ├── storage.conf │ └── registries.conf ├── emhttp/ │ └── plugins/podman/ # WebUI (PHP/JS), Phase 2+ ├── scripts/ │ ├── build-packages.sh │ └── release.sh └── CHANGELOG.md ``` ### 3.2 `.plg`-Manifest — Verantwortlichkeiten Das `.plg` ist eine deklarative XML-Datei, die von Unraids `plugin`-Kommando interpretiert wird. Verantwortlich für: - **Metadaten**: `` inkl. Mindest-Unraid-Version (Kernel-/glibc-Kompatibilität der Podman-Binaries). - **Entity-Variablen** (XML-Entities) für Basis-URL, Version, MD5-Summen — ermöglicht Update-Checks über den Plugin-Manager der GUI. - **``-Blöcke**: 1. Download & Installation aller acht `.txz`-Pakete (die sieben Komponenten plus das plugin-eigene `unraid-podman`-Scaffolding-Paket) einheitlich per `upgradepkg --install-new --reinstall` — funktioniert unverändert für Erstinstallation und Update (bestätigtes Muster echter Unraid-Plugins, siehe unten). 2. ``-Postinstall-Skript: Setzen von Berechtigungen, Anlegen der Verzeichnisstruktur unter `/boot/config/plugins/podman/` (nur beim ersten Install, idempotent; Configs nur kopiert, wenn nicht vorhanden — kein Überschreiben bestehender Nutzerkonfiguration bei Updates), Schreiben der Versions-Manifest-Datei, erster `rc.podman start`. - **Array-Start/Stop-Hook**: **kein** Eintrag in `/boot/config/go`. Stattdessen der offizielle Unraid-Plugin-Event-Mechanismus: `/usr/local/emhttp/plugins/podman/event/disks_mounted` (startet `rc.podman`, sobald Cache-Pools/Disks gemountet sind) und `.../event/stopping` (stoppt `rc.podman` synchron als allererster Schritt der Shutdown-Sequenz, bevor Unraid die zugrundeliegende Disk unmountet). Dieses Verzeichnis-Konvention wurde gegen den echten Quellcode etablierter Plugins verifiziert (u. a. `unassigned.devices`, das exakt dieselben `event/`-Hooks für `disks_mounted`/`stopping_svcs` verwendet) — kein `/boot/config/go`-Edit nötig, dadurch auch beim Deinstallieren nichts manuell rückgängig zu machen. - **``-Block für Deinstallation**: ruft zuerst `podman-uninstall-cleanup.sh` auf (stoppt `rc.podman`, räumt Laufzeitzustand auf), dann `removepkg` für alle acht Pakete. Lässt Nutzdaten unter `/mnt/*` und `/boot/config/plugins/podman/` standardmäßig **stehen** (explizite Option „Restlose Deinstallation inkl. Daten“ via `--purge-data`-Flag, kein automatisches Löschen von Nutzdaten). ### 3.3 Namens- und Versionskonventionen - Plugin-Name: `podman` (Namespace-Kollisionen mit CA-Plugins prüfen). - Paket-Versionierung getrennt von Plugin-Versionierung: `podman.plg` trägt eine eigene SemVer (`1.2.0`), referenziert aber exakte Upstream-Versionen der Binärpakete (`podman-5.x.x`, `netavark-1.x.x`, ...) — erlaubt Plugin-Patches (z. B. am Init-Skript) ohne Podman-Versionssprung. --- ## 4. Verzeichnislayout (vollständig) ### 4.1 Auf dem Flash-Device (`/boot`, persistent, klein) ``` /boot/config/plugins/podman/ ├── podman.cfg # Key=Value, analog docker.cfg (Storage-Pfad, Enable, Optionen) ├── autostart # eine Container-ID/-Name pro Zeile, Reihenfolge = Startreihenfolge ├── autostart-delay # optionale Wartezeiten pro Container (Key=Sekunden) ├── containers.conf # Vorlage, wird nach /etc/containers/ kopiert ├── storage.conf # Vorlage, referenziert podman.img-Pfad ├── registries.conf ├── policy.json ├── networks/ # persistierte netavark-Netzwerkdefinitionen (JSON) ├── backup/ │ ├── packages//*.txz # vorherige Paketversionen für Rollback │ └── config// # Config-Snapshots vor Updates └── plugin.log # Install-/Update-Log des Plugins selbst ``` `/boot/config/plugins/podman/` ist bewusst analog zu `/boot/config/plugins/dynamix.docker.manager/` gehalten (Wiedererkennung für erfahrene Unraid-Nutzer, Support-Vergleichbarkeit). ### 4.2 Laufzeit (RAM-Root, bei jedem Boot neu aufgebaut) ``` /etc/rc.d/rc.podman # Init-Skript (Symlink-Ziel oder direkt kopiert) /etc/containers/{containers,storage,registries}.conf # aus /boot kopiert /etc/containers/policy.json /etc/cni/ (falls CNI-Fallback statt netavark benötigt) /usr/bin/{podman,conmon,crun,fuse-overlayfs,passt,pasta} /usr/libexec/podman/{netavark,aardvark-dns} # matches upstream's own layout /usr/local/emhttp/plugins/podman/ # WebUI-Stub (statisch aus Paket) /var/run/podman/podman.sock # API-Socket (rootful) /var/log/podman -> /mnt/cache/system/podman/logs # Symlink, siehe Abschnitt 13 ``` ### 4.3 Persistente Nutzdaten (Cache-Pool bevorzugt, Array als Fallback) ``` /mnt/cache/system/podman/ # analog /mnt/user/system/docker/ ├── podman.img # Loopback-Datei, XFS (ftype=1) oder BTRFS │ # → gemountet auf /var/lib/containers/storage ├── logs/ │ ├── containers/.log │ └── podman-service.log └── networks/ # falls netavark-State nicht im Image liegt ``` **Warum Cache-Pool statt `/mnt/user`:** Der `overlay`-Storage-Driver benötigt native Dateisystemsemantik (d_type, xattrs, Hardlinks) ohne den FUSE-Layer von `shfs`. Das Docker-Vorbild löst das identisch mit `docker.img`/Directory-Modus auf einem echten Mountpoint. Nutzer ohne Cache-Pool müssen einen Array-Disk-Pfad (`/mnt/diskX/system/podman`) wählen können — mit deutlicher GUI-Warnung bzgl. Performance und Spin-up-Verhalten. --- ## 5. Paketmanagement ### 5.1 Zu paketierende Komponenten | Paket | Zweck | Bemerkung | |---|---|---| | `podman` | Kern-Binary | Statisch gegen möglichst wenige glibc-Versionen bauen oder gegen Unraid-Slackware-Base kompilieren | | `conmon` | Container-Monitor-Prozess | Pflicht | | `crun` (empfohlen) / `runc` | OCI-Runtime | `crun` bevorzugt (leichter, cgroup v2-freundlich, kompatibel zu Unraids Kernel-Config) | | `netavark` + `aardvark-dns` | Netzwerk-Backend + DNS | Ersetzt CNI-Plugins als Default seit Podman 4.x | | `containers-common` | Default-Configs (`containers.conf`, `seccomp.json`, `registries.conf`) | Wird als Vorlage übernommen, nicht als aktive Config | | `fuse-overlayfs` | Fallback-Storage-Driver | Für Rootless-Phase 2 vorbereitet, in Phase 1 optional | | `passt`/`pasta` | Rootless-Networking | Nachfolger von slirp4netns, Phase 2, aber Paket schon mitbauen (geringe Kosten) | | `catatonit` oder `tini` | Init-Prozess in Containern (optional, falls von Templates genutzt) | | ### 5.2 Build-Strategie - Eigene Build-Pipeline (containerisiert, z. B. in einem Slackware-kompatiblen Build-Container) statt manuellem Cross-Compile auf laufenden Unraid-Systemen. - Zielarchitektur: `x86_64` (Unraid unterstützt aktuell keine anderen Architekturen produktiv) — vereinfacht Matrix erheblich. - Statisch oder minimal dynamisch gelinkt (Go-Binaries von Podman/netavark/aardvark-dns sind ohnehin größtenteils statisch), um Abhängigkeits-Drift gegenüber der jeweils aktuellen Unraid-Slackware-Basis zu minimieren. - Jedes Paket als eigenständiges `.txz` mit Slackware-Standard-Metadaten (`slack-desc`, `doinst.sh`), damit `removepkg`/`upgradepkg` korrekt funktionieren. - Versions-Pinning: Das `.plg` referenziert exakte Paketversionen + MD5, kein „latest“-Pull zur Laufzeit — reproduzierbare Installationen, Voraussetzung für Rollback. ### 5.3 Abhängigkeitsprüfung - Preflight-Check im `.plg`-Postinstall: Kernel-Version, `cgroup v2` aktiv, vorhandene `iptables`/`nftables`-Binaries, freier Platz auf Zielpfad für `podman.img`. Bei Nichterfüllung: Installation abbrechen mit klarer GUI-Meldung statt eines halb-funktionsfähigen Zustands. --- ## 6. Start-/Stop-Skripte ### 6.1 `rc.podman` — Design analog zu `rc.docker` Klassisches Slackware-BSD-Init-Skript mit Case-Dispatch: ``` rc.podman start # Preflight → Storage mounten → API-Service starten → Autostart-Liste abarbeiten rc.podman stop # Container geordnet stoppen (Timeout) → API-Service stoppen → Storage unmounten rc.podman restart rc.podman status # Health-Check-Ausgabe für GUI/CLI ``` Kernschritte von `start` (konzeptionell, kein Code): 1. **Preflight** (`podman-preflight.sh`): prüft, ob konfigurierter Storage-Pfad (Cache-Pool/Disk) gemountet ist; falls `podman.img` fehlt → anlegen (Erstinstallation) mit konfigurierter Größe; falls vorhanden → per Loopback mounten. 2. **Config-Sync**: Kopiert `/boot/config/plugins/podman/*.conf` nach `/etc/containers/` (Boot-Config ist „Source of Truth“, Laufzeit-Kopie ist Cache). 3. **Socket-Service starten**: `podman system service` im Hintergrund (rootful Unix-Socket unter `/var/run/podman/podman.sock`), damit spätere WebUI/API-Konsumenten und CLI dieselbe Instanz sehen (statt pro Aufruf neuer Podman-Fork-Prozesse ohne gemeinsamen State — State liegt zwar in `/var/lib/containers`, ein Service vereinfacht aber Health-Checks, Event-Streaming und spätere WebUI-Anbindung). 4. **Netzwerke wiederherstellen**: netavark-Netzwerkdefinitionen aus `/boot/config/plugins/podman/networks/` nach `/etc/containers/networks/` synchronisieren. 5. **Autostart** (`podman-autostart.sh`, siehe Abschnitt 10). 6. **Statusdatei** schreiben (für GUI-Polling), Event ins Unraid-Log (`logger`). `stop` in umgekehrter Reihenfolge, mit konfigurierbarem Stop-Timeout pro Container (analog Dockers „Stop timeout“-Einstellung), danach `podman system service` beenden, zuletzt Loopback-Storage sauber unmounten (verhindert Dateisystemfehler im `podman.img`). ### 6.2 Boot-Integration - **Offizieller Unraid-Plugin-Event-Mechanismus**, kein `/boot/config/go`-Edit: Unraids `emhttpd` führt bei jedem Array-/Boot-Event automatisch jedes ausführbare Skript unter `/usr/local/emhttp/plugins//event/` aus, sofern vorhanden. Dieses Verhalten wurde gegen den echten Quellcode etablierter Plugins verifiziert (`unassigned.devices` nutzt exakt dieselbe Konvention für seine `disks_mounted`/`started`/`stopping_svcs`-Hooks). unraid-podman nutzt zwei Events: - `event/disks_mounted` — feuert, sobald Array-Disks **und** Cache-Pools gemountet sind (Unraids Event-Reihenfolge: `starting` → `array_started` → `disks_mounted` → `svcs_restarted` → `docker_started` → `libvirt_started` → `started`). Ruft `rc.podman start` im Hintergrund auf (`& disown`), damit der Array-Start nicht blockiert. - `event/stopping` — der **erste** Schritt der Shutdown-Sequenz (vor `stopping_docker`, `stopping_svcs`, `unmounting_disks`, `stopping_array`). Ruft `rc.podman stop` **synchron** auf, damit Container gestoppt und `podman.img` sauber unmountet sind, bevor Unraid die zugrundeliegende Disk/den Cache-Pool unmountet. Dadurch kein Start vor Verfügbarkeit von Cache/Array, und beim Deinstallieren ist nichts manuell rückgängig zu machen — die Hook-Skripte verschwinden automatisch mit `removepkg` des `unraid-podman`-Pakets (siehe Abschnitt 3.2). --- ## 7. Persistenz-Strategie (Zusammenfassung der Prinzipien) | Datenkategorie | Ablageort | Synchronisationsrichtung | |---|---|---| | Plugin-Settings (Storage-Pfad, Feature-Flags) | `/boot/config/plugins/podman/podman.cfg` | Boot → Laufzeit (read-only Kopie) | | Container-/Netzwerk-/Registry-Config | `/boot/config/plugins/podman/*.conf` | Boot → Laufzeit bei jedem Start | | Autostart-Liste & Reihenfolge | `/boot/config/plugins/podman/autostart` | Boot → Laufzeit; GUI schreibt zurück nach `/boot` | | Images, Container, named Volumes, Layer-Cache | `podman.img` auf Cache-Pool | Laufzeit-only, Backup optional via Snapshot | | Logs | Cache-Pool `logs/` | Laufzeit-only, Rotation | | Netzwerk-Definitionen (netavark JSON) | Boot-Kopie + Laufzeit-Kopie | bidirektional bei Änderung über GUI/CLI (Watcher oder expliziter „Apply“-Schritt) | Grundregel: **Jede Änderung, die ein Nutzer über CLI/GUI an persistenzrelevanten Objekten vornimmt (neues Netzwerk, geänderte Autostart-Reihenfolge), muss explizit oder per Watcher nach `/boot/config/plugins/podman/` zurückgeschrieben werden** — sonst geht sie beim nächsten Reboot verloren. Das ist der Kernunterschied zu Standard-Linux-Distributionen und der häufigste Fehlerquell-Kandidat bei Docker-artigen Unraid-Plugins. --- ## 8. Netzwerke - **Backend**: `netavark` + `aardvark-dns` (Podman-Default seit 4.x), kein CNI in Phase 1. - **Default-Bridge**: eigene Bridge `podman0` (nicht `docker0`), eigener privater Adressraum (konfigurierbar, Default-Vorschlag außerhalb von Dockers Default-Range, um Kollisionen bei Parallelbetrieb zu vermeiden). - **Custom Networks**: analog Docker-für-Unraid „Custom Networks“-Feature — GUI-Verwaltung in Phase 2, Config-Persistenz-Mechanismus aber bereits in Phase 1 vorsehen (Abschnitt 7). - **macvlan/ipvlan**: gleiche Kernel-Voraussetzungen wie bei Docker; bekannte Unraid-Falle „macvlan call trap“ (Kernel-Panic bei bestimmten NIC-Treibern) muss dokumentiert und im Preflight als Warnung ausgegeben werden, wenn macvlan-Netzwerke angelegt werden. - **iptables/nftables-Koexistenz mit Docker**: netavark legt eigene Chains an (`NETAVARK-*` Präfix); explizit verifizieren, dass diese nicht mit Dockers `DOCKER`-Chains oder Unraids eigenen Firewall-Regeln interferieren. Firewall-Reload durch Docker (bei dessen Start/Stop) darf Podman-Regeln nicht löschen und umgekehrt — ggf. eigenes `iptables-restore`-Hook nach jedem `rc.podman start`. - **DNS**: `aardvark-dns` für Container-zu-Container-Namensauflösung innerhalb benutzerdefinierter Netzwerke, analog Dockers eingebettetem DNS-Server. - **Port-Publishing**: Kollisionsprüfung mit bereits von Docker/anderen Diensten belegten Ports als GUI-seitige Validierung (spätere Phase), technisch bereits durch Kernel/`iptables` ohnehin erzwungen. --- ## 9. Volumes - **Named Volumes**: liegen innerhalb von `podman.img` unter `.../storage/volumes/`, damit sie vom selben Backup-/Rollback-Mechanismus wie Images erfasst werden. - **Bind-Mounts** auf Unraid-Shares (`/mnt/user/appdata/...`): empfohlener Standardweg für Nutzerdaten (analog Dockers „Path“-Mappings in Community-Applications-Templates), da `/mnt/user/appdata` bereits vom Nutzer im Backup-/Mover-/Cache-Konzept berücksichtigt wird. Bind-Mounts direkt gegen `/mnt/user` (FUSE) sind für Applikationsdaten unkritisch (kein Overlay-Storage-Anspruch), nur der Storage-Graph selbst braucht den echten Mount. - **Permissions/UID-Mapping**: Phase 1 rootful → Container laufen mit denselben UID/GID-Semantiken wie Docker rootful heute; keine zusätzliche User-Namespace-Remapping- Komplexität. Wird in Phase 2 (rootless) relevant. - **Migration-Hilfe (spätere Phase, nicht MVP)**: Werkzeug zum Referenzieren bestehender Docker-Bind-Mount-Pfade beim Anlegen äquivalenter Podman-Container, um Parallelbetrieb mit denselben Appdata-Verzeichnissen zu erleichtern (mit klarer Warnung vor gleichzeitigem Schreibzugriff durch zwei Runtimes). --- ## 10. Images - **Storage-Driver**: `overlay` (native, kein `fuse-overlayfs`) auf dem gemounteten `podman.img` — Performance-Parität mit Dockers Standard-Setup. - **Registries**: Default `registries.conf` mit Docker Hub + ggf. `ghcr.io`, `quay.io` vorkonfiguriert, kurz-Namen-Auflösung (`unqualified-search-registries`) explizit gesetzt statt interaktivem Prompt (der in einer daemonless/Skript-Umgebung nicht funktioniert). - **Pull/Push**: Standard-Podman-CLI-Semantik, keine Besonderheiten; Fortschrittsanzeige später über API-Service-Events für die WebUI nutzbar. - **Image-Größenmanagement**: `podman.img` ist eine feste/dynamisch wachsende Loopback-Datei — GUI muss Füllstand anzeigen und Vergrößern anbieten (analog Dockers „docker.img full“-Problem, das in der Community bekannt und schmerzhaft ist; hier von Anfang an mit klarer Fehlermeldung statt stillem Fehlschlag lösen, siehe Abschnitt 16). - **Community-Applications-Kompatibilität**: kein automatischer Import von Docker-Templates in Phase 1 (eigenes, separates Vorhaben); Podman-Images werden zunächst über CLI/eigene, minimale Template-Definition verwaltet. --- ## 11. Container (Lifecycle-Management) - Lifecycle-Operationen (`create`, `start`, `stop`, `restart`, `remove`, `exec`, `logs`, `inspect`) laufen über den in Abschnitt 6.1 gestarteten Podman-API-Service, nicht über Ad-hoc-CLI-Aufrufe aus der GUI heraus — ein gemeinsamer Service reduziert Race Conditions und ermöglicht Event-Streaming. - **Labels für GUI-Integration**: eigenes Label-Schema (z. B. `net.unraid.podman.icon`, `net.unraid.podman.webui`), angelehnt an, aber getrennt von Dockers `net.unraid.docker.*`-Konventionen, um spätere GUI-Icons/WebUI-Links analog zur Docker-Tab-Darstellung zu ermöglichen, ohne Namensraum-Kollisionen. - **Update-Strategie für Container-Images**: „Check for Updates“-Mechanismus (Digest-Vergleich gegen Registry) als spätere GUI-Funktion, technisch schon in Phase 1 per `podman image inspect`/`skopeo`-Vergleich vorbereitbar. --- ## 12. Autostart Da kein systemd existiert, wird Autostart **explizit vom Plugin verwaltet**, nicht von Podman selbst (Podman hat kein natives „restart on boot“ ohne generierte Unit-Files): - Flache Datei `/boot/config/plugins/podman/autostart`, eine Zeile pro Container (Name oder ID), Reihenfolge = Zeilen-Reihenfolge = Startreihenfolge — direktes Äquivalent zu Dockers `/boot/config/plugins/dynamix.docker.manager/docker-autostart` bzw. der DB-gestützten Nachfolgelösung. - Optionale, separate Delay-Datei für Wartezeiten zwischen Starts (Abhängigkeiten zwischen Containern, z. B. DB vor App). - `rc.podman start` liest die Liste sequenziell, startet jeden Container über den API-Service, protokolliert Erfolg/Fehler pro Eintrag, bricht bei Einzelfehlern **nicht** die gesamte Kette ab (ein defekter Container darf nicht alle anderen blockieren). - GUI-Checkbox „Autostart“ pro Container (spätere Phase) schreibt direkt in diese Datei zurück — kein separates DB-Layer in Phase 1, um Komplexität niedrig zu halten (kann bei Bedarf später durch strukturierteres Format, z. B. JSON, ersetzt werden). --- ## 13. Updates ### 13.1 Plugin-Update (Podman-Version, Init-Skripte) - Über den regulären Unraid-Plugin-Update-Mechanismus: `.plg` mit neuer Version + neuen Paket-URLs/MD5s wird erneut ausgeführt. - Vor jedem Update: automatisches Backup (siehe Abschnitt 14) der aktuellen `.txz`-Pakete und Config-Dateien. - **Container laufen während des Plugin-Updates weiter**, sofern nur Binaries ausgetauscht werden (laufende `conmon`/Container-Prozesse sind vom Dateisystem-Austausch unter `/usr/bin` isoliert, solange der Prozess nicht neu exec't wird) — erst ein nachfolgender `rc.podman restart` übernimmt die neue Podman-Version für den API-Service. Klar kommunizieren: Update ≠ sofortiger Container-Neustart. - Config-Migrationen (z. B. neues Feld in `containers.conf`) über ein Versions-gestempeltes Migrationsskript, das beim Postinstall geprüft wird (`podman.cfg` enthält `CONFIG_SCHEMA_VERSION`). ### 13.2 Container-Image-Updates - Getrennt vom Plugin-Update zu betrachten — reine Podman-Funktionalität (`podman pull` + Recreate), GUI-Komfortfunktion für Phase 2. --- ## 14. Rollback - **Paket-Rollback**: Vor jedem Update werden die aktuell installierten `.txz` nach `/boot/config/plugins/podman/backup/packages//` kopiert. Ein „Rollback“-Kommando (Postinstall-Skript, später GUI-Button) installiert diese Pakete erneut per `upgradepkg --reinstall`. - **Config-Rollback**: Vor jeder strukturellen Config-Änderung (Plugin-Update mit Schema-Migration) wird ein Snapshot nach `/boot/config/plugins/podman/backup/config//` geschrieben; Rollback kopiert diesen Snapshot zurück und setzt `CONFIG_SCHEMA_VERSION` entsprechend zurück. - **Storage-Rollback (Images/Container) ist explizit außerhalb des automatischen Rollbacks**: `podman.img` wird nicht bei jedem Plugin-Update gesichert (zu groß, zu I/O-intensiv). Stattdessen: dokumentierte, manuell auslösbare Snapshot-Funktion (z. B. `cp --reflink` auf BTRFS/ZFS-Cache-Pools, wo Reflinks verfügbar sind) als optionales Feature, kein Zwang. - Rollback-Historie begrenzen (z. B. letzte 3 Versionen), um den knappen Flash-Speicher nicht zu erschöpfen. --- ## 15. Logging - **Problem**: `/var/log` liegt im RAM und ist bei Reboot weg; `journald` existiert nicht, daher kann Podmans `journald`-Log-Driver nicht Default sein. - **Lösung**: Default-Log-Driver `k8s-file` (JSON-per-Zeile, Podman-nativ) mit Zielverzeichnis unter `/mnt/cache/system/podman/logs/containers/`, per Symlink von `/var/log/podman` dorthin erreichbar (einheitlicher Pfad für Tools, die `/var/log` erwarten, ohne RAM-Verlust). - **Log-Rotation**: eigener Cron-Eintrag (Unraid nutzt `cron` bereits für andere Plugins über `/boot/config/plugins/dynamix/`-Muster bzw. `/etc/cron.d`) mit `logrotate`-Konfiguration oder einfachem Größen-/Alter-basiertem Skript, da Flash/Cache-Platz begrenzt ist. - **Service-Log** (`podman system service`, `rc.podman`-Ausführung) getrennt von Container-Logs, ebenfalls auf Cache-Pool, zusätzlich Kurzfassung wichtiger Ereignisse (Start/Stop/Fehler) über `logger` in Unraids zentrales Syslog, damit sie in der Standard-„System Log“-GUI sichtbar sind. - **GUI-Zugriff (spätere Phase)**: Log-Viewer analog Dockers Container-Log-Fenster, liest direkt aus den `k8s-file`-Logs bzw. streamt über den API-Service. --- ## 16. Fehlerbehandlung ### 16.1 Startup-Robustheit - **Preflight-Checks** vor jedem `rc.podman start` (siehe 6.1): Storage-Mount, Speicherplatz auf `podman.img`, Kernel-Voraussetzungen, Socket-Verfügbarkeit (kein verwaister Socket von vorherigem Absturz). - **Storage-Korruption**: Falls `podman.img` beim Mount-Versuch Dateisystemfehler zeigt (`xfs_repair`/`btrfs check` schlägt fehl oder wird nicht automatisch ausgeführt) → Start abbrechen, klare GUI-Fehlermeldung mit Handlungsempfehlung (Recovery-Tool ausführen, Backup einspielen), **kein** automatisches Neuanlegen/Formatieren ohne Nutzerbestätigung (Datenverlustrisiko). - **Autostart-Fehler pro Container**: einzelner fehlgeschlagener Container wird geloggt und übersprungen, blockiert nicht die restliche Kette (siehe Abschnitt 12); nach N gescheiterten Autostart-Versuchen in Folge wird der Container automatisch aus der Autostart-Kette pausiert („Safe-Mode“ pro Container) mit GUI-Hinweis, um Boot-Loops/Ressourcenverschwendung zu vermeiden. ### 16.2 Laufzeitfehler - **Health-Check des API-Service**: `rc.podman status` prüft Socket-Erreichbarkeit und meldet Diskrepanzen (Prozess läuft, Socket tot o. ä.) verständlich. - **Firewall-/Netzwerk-Konflikte**: bei bekannten Fehlerbildern (z. B. Chain-Konflikt mit Docker, Port bereits belegt) definierte, für Menschen lesbare Fehlermeldungen statt roher `iptables`/Go-Stacktraces — Fehlerkatalog mit Klartext-Ursache + nächstem Schritt. - **Ressourcenerschöpfung** (`podman.img` voll): aktive Prüfung im Preflight und periodisch (Cron), GUI-Warnung bei > 85 % Füllstand, klare Anleitung zum Vergrößern des Loopback-Images (bekannte Docker-für-Unraid-Schwachstelle, hier von Anfang an proaktiv statt reaktiv lösen). - **Unraid-Benachrichtigungssystem**: kritische Fehler (Storage-Mount fehlgeschlagen, Autostart-Kette größtenteils fehlgeschlagen, `podman.img` > Schwellwert) werden über Unraids Standard-Notify-Mechanismus (`/usr/local/emhttp/webGui/scripts/notify`) ausgelöst, damit sie in GUI-Toasts, optional E-Mail/Pushover (falls vom Nutzer konfiguriert) erscheinen — kein Parallel-Notify-System erfinden. --- ## 17. Docker-Parallelbetrieb & spätere Deaktivierung ### Phase 1 — Parallelbetrieb (Pflicht) - Getrennte Storage-Roots (`docker.img` vs. `podman.img`), getrennte Default-Bridges, getrennte iptables-Chain-Präfixe, getrennte Log-Verzeichnisse — keine gemeinsam genutzten mutable Ressourcen außer read-only Kernel-Features (cgroups, netfilter). - Ressourcen-Konkurrenz (CPU/RAM/Disk-IO) ist erwartet und wird nicht technisch verhindert, aber in der GUI/Doku transparent gemacht. - Beide Runtimes dürfen gleichzeitig laufen, ohne dass Start/Stop des einen den anderen beeinflusst (insbesondere Firewall-Reloads, siehe Abschnitt 8). ### Phase 2+ — Docker optional deaktivierbar - Neuer Schalter (z. B. in den bestehenden Docker-Settings oder einem neuen „Container Engine“-Auswahlbereich): „Docker aktiviert / Podman aktiviert / beide“. - Deaktivierung von Docker bedeutet: `rc.docker stop` + Entfernen des Autostart-Hooks aus `/boot/config/go` für Docker, **kein** Deinstallieren des Docker-Plugins selbst (reversibel, Datenerhalt). - Voraussetzung für diese Phase: Podman-Pfad muss funktional äquivalent zu den von Nutzern tatsächlich genutzten Docker-Features sein (mind. Custom Networks, Autostart, Log-Viewer, Update-Check) — technisch als Gate, nicht nur als Empfehlung, im Freigabeprozess verankern. - CA-Kompatibilität (Community Applications erwartet aktuell Docker) ist der wahrscheinlich größte Blocker für „Docker komplett aus“ und sollte als eigenes Arbeitspaket (Template-Übersetzung Docker→Podman) vor Freigabe dieser Option behandelt werden. --- ## 18. Zukünftige WebUI ### 18.1 Architektur (Zielbild, Umsetzung nach MVP) - Eigene GUI-Seite unter `/usr/local/emhttp/plugins/podman/`, PHP + JS im bestehenden Unraid-„Dynamix“-Stil (Konsistenz mit restlicher GUI, wiederverwendbare CSS-/JS-Assets), analog zur Docker-Tab-Struktur (`dynamix.docker.manager`). - Backend-Kommunikation **nicht** über Shell-Exec von `podman`-CLI-Aufrufen aus PHP (fragil, schwer zu parallelisieren), sondern über den in Abschnitt 6.1 laufenden Podman-API-Service (Unix-Socket, REST, Docker-kompatible Teilmenge der API) — ermöglicht später auch Wiederverwendung bestehender Docker-API-kompatibler Frontend-Bibliotheken. - Echtzeit-Updates (Container-Status, Logs) über Server-Sent Events oder Polling gegen den API-Service, kein WebSocket-Zwang in Phase 1 der WebUI (geringerer Implementierungsaufwand, ausreichend für Status-Refresh im Sekundenbereich). ### 18.2 Funktionsumfang, gestaffelt 1. **Stufe 1**: Read-only Übersicht (laufende Container, Images, Netzwerke), Start/Stop/Restart einzelner Container. 2. **Stufe 2**: Container-Erstellung über einfache Formulare (Image, Ports, Volumes, Env, Labels), Autostart-Verwaltung direkt in GUI (schreibt in Abschnitt-12-Datei). 3. **Stufe 3**: Log-Viewer, Netzwerk-Verwaltung (Custom Networks/macvlan-Anlage), Update-Check pro Container. 4. **Stufe 4**: Community-Applications-Anbindung/Template-Import (separates Vorhaben). ### 18.3 Sicherheitsaspekt der WebUI - API-Service-Socket nur lokal (`/var/run/podman/podman.sock`), keine TCP-Exposition nach außen ohne explizite, abgesicherte Opt-in-Konfiguration (TLS + Auth), da rootful Podman-API faktisch Root-Äquivalent ist — gleiche Vorsicht wie beim Docker-Socket. --- ## 19. Sicherheitsbetrachtungen (Phase 1, rootful) - Rootful Podman-API-Socket ist funktional gleichwertig zu `docker.sock` bzgl. Angriffsfläche (Zugriff = Root auf dem Host) — Berechtigungen auf den Socket (`0660`, Gruppe analog `docker`-Gruppe, z. B. neue Gruppe `podman`) restriktiv setzen. Achtung Docker Sock: root Äquivalenz, daher keine ungeprüfte Gruppenmitgliedschaft für WebUI-Prozesse vergeben, die nicht ohnehin schon rootäquivalent laufen (`emhttpd` läuft unter Unraid ohnehin als root, insofern kein zusätzliches Risiko gegenüber Docker heute — aber explizit dokumentieren, nicht stillschweigend voraussetzen). - Keine automatische, ungefragte Netzwerk-Exposition von Container-Ports über die Standard-Bridge hinaus. - Rollback-/Backup-Mechanismus darf keine Zugangsdaten (Registry-Logins in `auth.json`) ungesichert auf dem FAT32-Flash ablegen, ohne dass sich der Nutzer dessen bewusst ist (Flash ist physisch leicht auslesbar) — ggf. Hinweis, sensible Registry-Credentials bevorzugt nicht dauerhaft dort zu speichern, oder Ablage ausschließlich auf dem Cache-Pool statt `/boot`. --- ## 20. Phasenplan | Phase | Inhalt | |---|---| | **MVP (Phase 1)** | Plugin-Grundgerüst, Pakete, `rc.podman`, Storage auf Cache-Pool, Autostart-Datei, Basis-Logging, Preflight/Fehlerbehandlung, Rollback für Pakete/Config, **keine WebUI** (CLI-only, `podman` direkt nutzbar) | | **Phase 2** | WebUI Stufe 1–2, Custom-Networks-Verwaltung, Log-Viewer, Storage-Snapshot-Feature | | **Phase 3** | Rootless-Option, Docker-Deaktivierungs-Schalter, CA-Template-Kompatibilitätsschicht | | **Phase 4** | Pods/Quadlet-artige Konzepte (ohne systemd nur eingeschränkt sinnvoll — ggf. eigenes Skript-Äquivalent statt echter Quadlet/systemd-Generatoren) | --- ## 21. Offene Fragen / Risiken (zur Klärung vor Implementierungsstart) 1. Ziel-Podman-Version und Update-Kadenz (Tracking von Upstream-CVEs erfordert Paket-Update-Prozess, nicht nur Erstrelease). 2. Cache-Pool-Pflicht vs. Array-Fallback für `podman.img` — Nutzer ohne Cache-Pool sind bei Docker heute ebenfalls im Nachteil; gleiche Community-Erwartungshaltung übernehmen oder verbessern? 2a. Verhalten bei mehreren Cache-Pools (Unraid 6.9+ unterstützt mehrere benannte Pools) — welchen Pool per Default vorschlagen, wie in GUI wählbar machen. 3. `podman system service` als Dauerprozess vs. Socket-Activation-artiges On-Demand-Start (ohne systemd nur eingeschränkt nachbaubar) — Dauerprozess ist der pragmatischere Start, sollte aber gegen Ressourcenverbrauch im Idle evaluiert werden. 4. Genaues Verhalten bei gleichzeitigem Boot von Docker und Podman bzgl. iptables-Reihenfolge/Locking (`iptables`-Aufrufe sind nicht atomar über mehrere Prozesse hinweg) — ggf. Locking-Strategie (`xtables_lock`) explizit prüfen. 5. Lizenz-/Signatur-Anforderungen für Community-Verbreitung des Plugins (Unraid-Plugin-Repository-Richtlinien, Signierung der `.plg`/Pakete).