Files
unraid-podman/docs/ARCHITECTURE.md
T
maggesandClaude Sonnet 5 2b79411b68 Replace vendored docker-compose with podman-compose
podman-compose and docker-compose aren't discovered the same way by
`podman compose` - verified live (a fake-binary test reading podman's own
provider-search error output) that docker-compose is searched for by
exact path across a fixed list of CLI-plugin directories, while
podman-compose is instead looked up as a plain command on $PATH. This
package installs to /usr/local/bin/podman-compose accordingly, not under
any cli-plugins/ directory.

Unlike docker-compose (a single static Go binary), podman-compose is a
Python script with two runtime dependencies neither of which ship with
Unraid's own Python3 - PyYAML and python-dotenv, vendored here as plain
pure-Python source (no C extension build; PyYAML's own fallback handles
its optional C accelerator being absent).

Verified end-to-end on a real host: with the previous docker-compose
binary temporarily moved aside to confirm podman-compose was actually the
one invoked, `podman compose up/ps/down` ran a real compose project
correctly, including a live HTTP check against the started service.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 18:35:38 +00:00

39 KiB
Raw Permalink Blame History

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
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 <FILE>-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: <plugin name="podman" version="..." pluginURL="..." min="..." /> 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.
  • <FILE>-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. <INLINE>-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.
  • <FILE Run="/bin/bash" Method="remove">-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/<version>/*.txz   # vorherige Paketversionen für Rollback
│   └── config/<timestamp>/        # 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/<id>.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)
nftables Firewall-Backend für netavark Pflicht seit netavark 2.0 (iptables-Treiber entfernt); Unraid liefert kein nft mit — als offizielles Slackware-Paket vendored, nicht selbst gebaut
podman-compose External-Compose-Provider für podman compose podman compose hat keine eigene Compose-Implementierung, sondern sucht ein Kommando namens podman-compose auf $PATH (live verifiziert — anders als das ältere, ebenfalls unterstützte docker-compose, das stattdessen in festen CLI-Plugin-Pfaden gesucht wird); ohne dieses Paket schlägt jede Compose-Panel-Aktion auf einem frischen Unraid-Install fehl. Python-Skript, vendored zusammen mit PyYAML/python-dotenv als reines Python-Source (kein C-Build)

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/<name>/event/<eventname> 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: startingarray_starteddisks_mountedsvcs_restarteddocker_startedlibvirt_startedstarted). 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.
  • Firewall-Treiber: netavark >= 2.0 hat seinen iptables-Treiber ersatzlos entfernt — nur noch nftables oder firewalld (Letzteres braucht systemd/dbus, hat Unraid nicht). nftables ist also zwingend, aber Unraid OS bringt das nft-Binary selbst nicht mit (nur das ältere iptables/iptables-nft) — muss vom Plugin bereitgestellt werden (verifiziert per Live-Test: podman run mit Port-Publishing scheitert ohne nft mit „Must provide a valid firewall backend“).
  • 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.
  • Signatur-Policy (config/policy.json): insecureAcceptAnything als Default — akzeptiert Images ohne Signaturprüfung, entspricht Dockers heutigem Standard-Vertrauensmodell auf Unraid. Vor einem 1.0-Release erneut bewerten, falls signierte Images ein Ziel werden. Die Datei selbst darf keine Kommentarfelder enthalten (containers/images Policy-Parser lehnt unbekannte Top-Level-Keys wie _comment strikt ab) — Begründung lebt deshalb hier, nicht in der Datei.

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/<alte-version>/ 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/<timestamp>/ 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 12, 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).