====== Root³ Solutions LibreChat Deployment Guide ====== **Stack**: LibreChat + LM Studio (Qwen3 27B) + ComfyUI MCP + Admin-Panel **Host**: 192.168.1.203 — Nobara Linux, Podman (kein Docker) **Installiert**: 2026-10-19 / finalisiert 2026-10-20 **URLs**: App http://192.168.1.203:3080 — Admin-Panel http://192.168.1.203:3090 **Konto**: info@root3.de (ADMIN — erster registrierter Nutzer) ===== 1. Verzeichnisstruktur ===== Ein Ordner, alles drin. Alle Secrets liegen NUR in .env — compose.yaml referenziert sie als ${VARS}, nie hartkodiert [1]. ~/librechat/ ├── compose.yaml # Podman Compose — 3 Services (api + mongodb + admin-panel) ├── librechat.yaml # LibreChat-Konfig — Endpoints, MCP, Interface ├── boot-librechat.sh # Login-Autostart-Skript (LLM zuerst) ├── .env # ALLE Secrets + Umgebungsvariablen (in api und Panel eingebunden) ├── root_solutions_logo.svg # Dein Logo — ersetzt die eingebaute logo.svg ├── index.html # Gepatchtes LibreChat-Index (linkt custom.css) └── custom.css # Logo-Größe + Zentrierung Override ===== 2. compose.yaml (Komplett) ===== Drei Services: LibreChat-API, MongoDB und das separate Admin-Panel [1]. Keine Secrets in dieser Datei — nur eine ${VARIABLE}-Referenz für das Panel-Session-Secret, die podman-compose bei der Container-Erstellung aus .env auflöst (verifiziert funcionando). services: api: image: ghcr.io/danny-avila/librechat:latest container_name: librechat-app ports: - "3080:3080" depends_on: - mongodb extra_hosts: - "host.containers.internal:host-gateway" volumes: # Konfig + Branding (Read-only Bind-Mounts) - ./librechat.yaml:/app/librechat.yaml:ro - ./root_solutions_logo.svg:/app/client/dist/assets/logo.svg:ro - ./index.html:/app/client/dist/index.html:ro - ./custom.css:/app/client/dist/custom.css:ro # Dauerhafte Daten (Named Volumes — überleben Container-Neuanlage) - librechat-images:/app/client/public/images - librechat-uploads:/app/api/data/uploads - librechat-logs:/app/api/logs # Alle Secrets/Env — einzige Wahrheit ist ./.env - ./.env:/app/.env:ro environment: - HOST=0.0.0.0 - PORT=3080 - MONGO_URI=mongodb://mongodb:27017/LibreChat - ALLOW_REGISTRATION=true restart: "no" mongodb: image: docker.io/library/mongo:latest container_name: librechat-db volumes: - librechat-db-data:/data/db restart: "no" admin-panel: image: registry.librechat.ai/librechat-ai/librechat-admin-panel:latest container_name: librechat-admin ports: - "3090:3000" depends_on: - api extra_hosts: - "host.containers.internal:host-gateway" volumes: - ./.env:/app/.env:ro environment: - PORT=3000 - SESSION_SECRET=${ADMIN_PANEL_SESSION_SECRET} - API_SERVER_URL=http://librechat-app:3080 - VITE_API_BASE_URL=http://192.168.1.203:3080 restart: "no" volumes: librechat-db-data: name: librechat-db-data librechat-images: name: librechat-images librechat-uploads: name: librechat-uploads librechat-logs: name: librechat-logs **Wichtige Design-Entscheidungen:** * //restart: "no"// überall — Start wird vom Boot-Skript gesteuert; keine unerwarteten Auto-Neustarts (passt zu deinem open-webui-Pattern) * //host.containers.internal:host-gateway// — Container erreichen LM Studio (:1234) und ComfyUI (:8188) auf dem Host per Namen, ohne harte LAN-IP * Named Volumes für alle dauerhaften Daten — Löschen/Erneuern von Container berührt nie Chat-Verlauf, Uploads oder Datenbank [2] * Vollqualifizierte Image-Namen (docker.io/library/mongo:latest) — verhindert den interaktiven "Image auswählen"-Prompt in unbeaufsichtigten Skripten * Das Admin-Panel hat eine **eigene URL** (:3090), ist kein Tab in der App — das ist LibreChars offizielle Architektur [1]; Nutzer-Verwaltung existiert nur dort (oder via Registrierung bzw. den offiziellen create-user/delete-user-CLI-Skripts) ===== 3. .env-Datei (Komplett — ALLE Secrets hier) ===== # ~/librechat/.env HOST=0.0.0.0 PORT=3080 MONGO_URI=mongodb://mongodb:27017/LibreChat ALLOW_REGISTRATION=true JWT_SECRET=super_secret_token_string_generation_12345 REFRESH_TOKEN_SECRET=super_secret_refresh_token_string_54321 # ⚠️ DIE Variable, die dieser Image-Build für Sign-in tatsächlich liest (siehe 8a): JWT_REFRESH_SECRET=super_secret_refresh_token_string_54321 ADMIN_PANEL_SESSION_SECRET=08513016b4e0f9fc9d3466f4d10e740efdf490c6d2fae7bc86536b2bb7fc5ea3 ADMIN_PANEL_PORT=3090 ADMIN_PANEL_URL=http://192.168.1.203:3090 **Hinweise:** * Wird in BEIDE Container (api und admin-panel) eingebunden (.env:/app/.env:ro) — der Auth-Code der api liest Secrets aus dieser Datei [3] * ALLOW_REGISTRATION=true schaltet den Sign-up-Link frei; nach Konto-Anlage auf false stellen, um die Tür zu verschließen (Nutzer können weiterhin über Panel/CLI angelegt werden) * ADMIN_PANEL_PORT / ADMIN_PANEL_URL werden aktuell von keinem Service gelesen — als Absichtsdokumentation behalten ===== 4. librechat.yaml (Komplett) ===== version: 1.1.2 interface: privacyPolicy: externalUrl: 'https://yourcompany.com' openNewTab: true termsOfService: externalUrl: 'https://yourcompany.com' openNewTab: true endpoints: custom: - name: "LM Studio" # host.containers.internal = der Host, vom Container aus gesehen baseURL: "http://host.containers.internal:1234/v1" apiKey: "lm-studio" models: fetch: true # Live-Modellliste aus LM Studio default: ["qwen/qwen3.8-27b"] # deine tatsächliche Modell-ID titleConvo: true summarize: false mcpServers: comfyui: type: stdio command: npx args: - "-y" - "comfyui-mcp" # echtes npm-Paket (Oktober 2026 verifiziert) - "--comfyui-url" - "http://host.containers.internal:8188" **Hinweise:** * //baseURL// ist der korrekte Feldname (nicht "url") [4] * //fetch: true// füllt die Modell-Auswahl automatisch aus dem, was in LM Studio geladen ist * "mcp-server-comfyui" existiert NICHT auf npm — //comfyui-mcp// ist das echte Paket, unterstützt Remote-ComfyUI via --comfyui-url [5] * Der erste MCP-Aufruf braucht Internet im Container (npx-Download); spätere Aufrufe sind gecacht ===== 5. Logo-Größe (custom.css) ===== LibreChat rendert logo.svg in fester kleiner Größe, ohne Config-Schalter. Lösung: gepatchtes index.html lädt ein zusätzliches Stylesheet, das die img-Regel überschreibt [6]. ==== 5a. So wurde es eingerichtet ==== # 1) Die echte index.html aus dem Container holen (während er läuft) podman exec librechat-app cat /app/client/dist/index.html > ~/librechat/index.html # 2) Einen Link-Tag in einfügen sed -i 's|||' ~/librechat/index.html # 3) Das Override selbst (Datei: ~/librechat/custom.css) cat > ~/librechat/custom.css <<'EOF' img[src*="logo.svg"] { height: 140px !important; width: auto !important; display: block !important; margin-left: auto !important; margin-right: auto !important; } EOF ==== 5b. Nachjustierung ==== px-Wert in //​~/librechat/custom.css// ändern → Browser hart neu laden (Strg+Shift+R). Bind-Mount liest live — kein Container-Neuaufbau nötig. ⚠️ **Nach jedem LibreChat-Image-Update** index.html aus dem frischen Container neu extrahieren und die sed-Zeile erneut anwenden, dann einmal down/up (sonst geht das Logo-Patch mit dem neuen Image verloren). ===== 6. boot-librechat.sh (Finale Version) ===== Läuft über Nobara Login Scripts (System-Einstellungen → Autostart → Login-Skripte). Reihenfolge wie dein bestehendes start-ai-new.sh: LLM zuerst, dann alle drei Container, dann Health-Check. Idempotent — mehrfaches Auslösen beim Login ist sicher. #!/bin/bash # boot-librechat.sh — startet LibreChat-Stack nach dem Login (LM Studio zuerst) LOG=/var/log/librechat-boot.log exec >> "$LOG" 2>&1 echo "=== $(date) librechat-boot ===" PATH="$HOME/.local/bin:$PATH" MODEL="qwen/qwen3.8-27b" # --- 1. Läuft der LM-Studio-Server? ----------------------------------- if ! curl -s --connect-timeout 2 http://localhost:1234/v1/models > /dev/null; then echo "⏳ LM Studio nicht erreichbar — Server wird gestartet..." lms server start || echo "⚠️ lms server start fehlgeschlagen." for i in $(seq 1 15); do curl -s --connect-timeout 2 http://localhost:1234/v1/models > /dev/null && break sleep 2 done fi # --- 2. Ist ein Modell geladen? (no-op, falls schon im RAM) ---------- echo "⏳ Modell wird in LM Studio geladen..." lms load "$MODEL" || echo "⚠️ lms load fehlgeschlagen." sleep 5 # --- 3. Alle LibreChat-Container starten ------------------------------- if podman ps -a --filter "name=librechat-app" --format '{{.Names}}' | grep -q librechat-app; then echo "📦 LibreChat-Container werden gestartet..." podman start librechat-app librechat-db librechat-admin 2>&1 else echo "⚠️ Container nicht gefunden — erst via Compose anlegen..." cd ~/librechat && podman-compose --in-pod true up -d \ || { echo "❌ Compose fehlgeschlagen."; exit 1; } fi # --- 4. Health-Check --------------------------------------------------- for i in $(seq 1 10); do if curl -s --connect-timeout 2 http://localhost:3080/ > /dev/null; then echo "✅ LibreChat-API ist erreichbar." break fi [ "$i" -eq 10 ] && { echo "❌ API tot — letzte 30 Log-Zeilen:"; podman logs --tail 30 librechat-app; exit 1; } sleep 5 done echo "=== $(date) fertig ===" **Autostart-Einrichtung (einmalig, erledigt):** * Nobara System-Einstellungen → Autostart → + Neu hinzufügen → Login-Skript → //​~/librechat/boot-librechat.sh// → Ja sagen für Ausführbarkeit * Zum Deaktivieren: Eintrag löschen, oder //​rm ~/librechat/boot-librechat.sh// **Log-Datei:** //​/var/log/librechat-boot.log// — //​tail -f /var/log/librechat-boot.log// ===== 7. Befehlsreferenz ===== ^ Aktion ^ Befehl ^ | --- | --- | ^ Stack starten (manuell) ^ podman start librechat-app librechat-db librechat-admin ^ ^ Stack stoppen ^ podman stop librechat-app librechat-db librechat-admin ^ | up/down der Container nach yaml-/env-Änderung | cd ~/librechat && podman-compose --in-pod true down && podman-compose --in-pod true up -d ^ ^ LibreChat-Image aktualisieren (behalten alle Daten) ^ cd ~/librechat && podman-compose --in-pod true down && podman pull ghcr.io/danny-avila/librechat:latest && podman-compose --in-pod true up -d ^ | --- | --- | ^ App-Logs ansehen ^ podman logs --tail 100 librechat-app ^ ^ Mongo-Logs ansehen ^ podman logs --tail 50 librechat-db ^ ^ Panel-Logs ansehen ^ podman logs --tail 50 librechat-admin ^ ^ Boot-Log ansehen ^ tail -f /var/log/librechat-boot.log ^ ^ Pod-Status prüfen ^ podman pod ps ^ ^ Stack entfernen, DATEN behalten ^ cd ~/librechat && podman-compose --in-pod true down ^ ^ Komplett löschen inkl. Daten ^ oben + podman volume rm librechat-db-data librechat-images librechat-uploads librechat-logs ^ | --- | --- | ^ Alle Nutzer + Rollen auflisten ^ podman exec librechat-db mongosh --quiet --eval 'db.getSiblingDB("LibreChat").users.find({}, {email:1, roles:1}).toArray()' ^ ^ Live-Modelle von LM Studio prüfen ^ curl -s http://localhost:1234/v1/models ^ **Hinweise:** * "up/down nach yaml/env-Änderung" = wendet deine Config-Änderungen an (das ist deine Zeile — Achte auf das korrekte Bindestrich in -in-pod) * "Image aktualisieren" holt zusätzlich die neue Version; danach index.html fürs Logo neu extrahieren (Abschnitt 5b) * Alle down/rm-Befehle berühren Named Volumes nie — Daten sind sicher ===== 8. Fehlerbehebung ===== ==== 8a. Login liefert 500 "secretOrPrivateKey must have a value" ==== **Ursache:** Dieser Image-Build signiert Refresh-Tokens mit //​process.env.JWT_REFRESH_SECRET//, nicht REFRESH_TOKEN_SECRET (bestätigt in node_modules/@librechat/data-schemas/dist/index.mjs:5989). Registrierung funktionierte, weil dort kein Token ausgestellt wird. **Lösung:** JWT_REFRESH_SECRET= in .env + Neuaufbau. ✅ Angewendet. ==== 8b. "Image auswählen"-Prompt hängt Skripte auf ==== **Ursache:** Nackte Image-Namen lösen Podmans interaktiven Registry-Auswahlmenü aus; ohne TTY blockt es ewig. **Lösung:** Vollqualifizierte Namen in compose.yaml (docker.io/library/mongo:latest, ghcr.io/danny-avila/librechat:latest). ✅ Angewendet. ==== 8c. Code-Interpreter-Image 403 Forbidden ==== **Ursache:** ghcr.io/danny-avila/code-interpreter:latest existiert nicht mehr im Registry; die offizielle Compose-Datei liefert es auch nicht mehr [1]. **Lösung:** Service entfernt — für Chat + ComfyUI-MCP nicht nötig. ✅ Angewendet. ==== 8d. Logo auf der Login-Seite zu klein ==== **Ursache:** Eingebaute CSS skaliert logo.svg fix; kein Config-Schalter im interface-Objekt vorhanden. **Lösung:** Gepatchtes index.html → custom.css → img[src*="logo.svg"]-Override, 140px + zentriert. ✅ Im Browser verifiziert. ==== 8e. podman-compose --pod wird nicht erkannt ==== **Ursache:** Das dnf-Paket (v1.6.0) ist containers/podman-compose v1.x — anderer Flag-Name als die pipx-Version. **Lösung:** //​--in-pod true// vor dem Subkommando verwenden: podman-compose --in-pod true up -d. ✅ Angewendet. ==== 8f. "agent_id is required in request body" ==== **Ursache:** Nachricht wurde gesendet, bevor im Modell-Picker der LM-Studio-Endpoint ausgewählt war — Anfrage lief an den Agents-Endpunkt. **Lösung:** Neue Unterhaltung, Modell zuerst explizit wählen. ✅ Behoben. ==== 8g. Admin-Panel-Image 403 auf ghcr ==== **Ursache:** ghcr.io/danny-avila/librechat-admin-panel:latest existiert dort nicht; korrektes Registry laut offizieller Compose ist registry.librechat.ai/librechat-ai/librechat-admin-panel:latest [1]. **Lösung:** Image-Referenz korrigiert. ✅ Container läuft, Panel erreichbar unter :3090. ==== 8h. Logs zeigen "OpenID check failed: 500 .../openid/check" ==== **Ursache:** Bekannter Upstream-Bug — LibreChat-Issue #14504, gefixt in PR #14507. Der Maintainer bestätigt: Log-Rauschen, NICHT der Login-Blocker (das Panel behandelt jede nicht-ok-Antwort als "SSO unavailable" und fällt auf E-Mail/Passwort zurück). **Aktion:** Keine — ignorieren, bis ein Image-Update den Fix enthält. ==== 8i. Admin-Panel-Login: Formular wird abgeschickt, passiert nichts ==== ⚠️ **OFFENER PUNKT.** API-Logs zeigen wiederholt //​[Login] [Login successful] [info@root3.de]// — Zugangsdaten akzeptiert, Auth funktioniert; das Panel stellt aber keine Browser-Session auf. Verdacht Nr. 1: Session-Cookies mit Secure-Flag über plain HTTP (Kandidaten: SESSION_COOKIE_SECURE=false / ADMIN_PANEL_SESSION_COOKIE_SECURE=false in .env) oder Panel-internes Session-Wiring. Zweite offene Prüfung: users.find({}, {email:1, roles:1}) — sicherstellen, dass genau ein Nutzer die ADMIN-Rolle trägt (laut #14504-Thread bekommt nur der erste registrierte Nutzer sie). **Status:** ungelöst — nächster Schritt: Network-Tab auf :3090/login (fehlgeschlagene Anfrage + Statuscode) plus Logs von librechat-admin während eines Login-Versuchs. ===== 9. Statusübersicht ===== ^ Komponente ^ Status ^ Hinweis ^ | --- | --- | --- | ^ Logo (groß + zentriert) ^ ✅ im Browser verifiziert ^ custom.css, 140px ^ ^ Auth: Registrierung + Login ^ ✅ funktioniert ^ JWT_REFRESH_SECRET-Fix ^ ^ LM-Studio-Chat ^ ✅ "Hello!"-Antwort bestätigt ^ Qwen3 27B via fetch:true ^ | --- | --- | --- | ^ ComfyUI MCP-Server ^ ✅ "running on stdio" in Logs ^ comfyui-mcp-Paket ^ ^ Admin-Konto ^ ✅ info@root3.de (erster Nutzer = ADMIN) ^ Panel hat eigene URL :3090 ^ ^ Admin-Panel-Service ^ ✅ läuft, erreichbar unter :3090 ^ Login noch offen (8i) ^ | --- | --- | --- | ^ Autostart (LLM zuerst) ^ ✅ Nobara Login-Skript ^ /var/log/librechat-boot.log ^ ^ Datenbeständigkeit ^ ✅ Named Volumes ^ Überleben Neuanlage/Updates ^ ^ Podman-Desktop-Gruppierung ^ ✅ pod_librechat, 3 Container ^ --in-pod true ^ | --- | --- | --- | ^ ComfyUI-Bildgenerierung End-to-End ^ ⏳ noch nicht getestet ^ letzter offener Funktionstest ^ ======