====== 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 ^
======