====== Root³ Solutions LibreChat Deployment Guide ======
**Stack**: LibreChat + LM Studio (Qwen3 27B) + ComfyUI MCP + Admin Panel
**Host**: 192.168.1.203 — Nobara Linux, Podman (no Docker)
**Deployed**: 2026-10-19 / finalized 2026-10-20
**URLs**: App http://192.168.1.203:3080 — Admin Panel http://192.168.1.203:3090
**Account**: info@root3.de (ADMIN — first registered user)
===== 1. Folder Structure =====
One folder, everything inside it. All secrets live ONLY in .env — compose.yaml references them as ${VARS}, never hardcoded [1].
~/librechat/
├── compose.yaml # Podman Compose — 3 services (api + mongodb + admin-panel)
├── librechat.yaml # LibreChat config — endpoints, MCP, interface
├── boot-librechat.sh # Login autostart script (LLM-first ordering)
├── .env # ALL secrets + env vars (mounted into both api and panel)
├── root_solutions_logo.svg # Your logo — overwrites the built-in logo.svg
├── index.html # Patched LibreChat index (links custom.css)
└── custom.css # Logo size + centering override
===== 2. compose.yaml (Complete) =====
Three services: LibreChat API, MongoDB, and the separate Admin Panel [1]. No secrets in this file — only one ${VAR} reference for the panel session secret, which podman-compose expands from .env at create time (verified working).
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:
# Config + 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
# Durable data (named volumes — survive container recreation)
- librechat-images:/app/client/public/images
- librechat-uploads:/app/api/data/uploads
- librechat-logs:/app/api/logs
# All secrets/env — single source of truth is ./.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
**Key design decisions:**
* //restart: "no"// everywhere — startup is owned by the boot script; no surprise auto-restarts (matches your open-webui pattern)
* //host.containers.internal:host-gateway// — containers reach LM Studio (:1234) and ComfyUI (:8188) on the host by name, no hardcoded LAN IP
* Named volumes for all durable data — deleting/recreating containers never touches chats, uploads, or the database [2]
* Fully-qualified image names (docker.io/library/mongo:latest) — prevents the interactive "select an image" prompt from hanging unattended scripts
* Admin panel is a **separate URL** (:3090), not a tab inside the app — that is LibreChat's official architecture [1]; user creation/management only exists there (or via registration, or the official create-user/delete-user CLI scripts)
===== 3. .env File (Complete — ALL Secrets Here) =====
# ~/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
# ⚠️ THE one this image build actually reads for sign-in (see troubleshooting 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
**Notes:**
* Mounted into BOTH the api and admin-panel containers (./.env:/app/.env:ro) — the api's auth code reads secrets from this file [3]
* ALLOW_REGISTRATION=true is what makes the Sign-up link appear; flip to false once your account exists to lock the door (users can still be created via the panel/CLI)
* ADMIN_PANEL_PORT / ADMIN_PANEL_URL are currently unused by any service — kept as documentation of intent
===== 4. librechat.yaml (Complete) =====
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 = the host machine, from inside the container
baseURL: "http://host.containers.internal:1234/v1"
apiKey: "lm-studio"
models:
fetch: true # live model list from LM Studio
default: ["qwen/qwen3.8-27b"] # your actual model ID
titleConvo: true
summarize: false
mcpServers:
comfyui:
type: stdio
command: npx
args:
- "-y"
- "comfyui-mcp" # real npm package (verified Oct 2026)
- "--comfyui-url"
- "http://host.containers.internal:8188"
**Notes:**
* //baseURL// is the correct field name (not "url") [4]
* //fetch: true// auto-populates the model picker from whatever LM Studio has loaded
* "mcp-server-comfyui" does NOT exist on npm — //comfyui-mcp// is the real package, supports remote ComfyUI via --comfyui-url [5]
* First MCP call needs internet inside the container (npx download); later calls are cached
===== 5. Logo Sizing (custom.css) =====
LibreChat renders logo.svg at a fixed small size with no config key to change it. Fix: patched index.html loads an extra stylesheet, which overrides the img rule [6].
==== 5a. How it was set up ====
# 1) Copy the real index.html out of the container (while running)
podman exec librechat-app cat /app/client/dist/index.html > ~/librechat/index.html
# 2) Inject a link tag into
sed -i 's|||' ~/librechat/index.html
# 3) The override itself (file: ~/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. Tuning ====
Edit the px value in //~/librechat/custom.css// → hard refresh browser (Ctrl+Shift+R). Bind mount reads live — no container recreate needed.
⚠️ **After every LibreChat image update**, re-extract index.html from the fresh container and re-apply the sed line, then down/up once (logo patch would otherwise revert on new images).
===== 6. boot-librechat.sh (Final Version) =====
Runs via Nobara Login Scripts (System Settings → Autostart → Login Scripts). Ordering matches your existing start-ai-new.sh pattern: LLM first, then all three containers, then health check. Idempotent — safe to fire multiple times at login.
#!/bin/bash
# boot-librechat.sh — start LibreChat stack after login (LM Studio first)
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. LM Studio server up? ------------------------------------------
if ! curl -s --connect-timeout 2 http://localhost:1234/v1/models > /dev/null; then
echo "⏳ LM Studio not responding — starting server..."
lms server start || echo "⚠️ lms server start failed."
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. Model loaded? (no-op if already in RAM) ------------------------
echo "⏳ Loading model into LM Studio..."
lms load "$MODEL" || echo "⚠️ lms load failed."
sleep 5
# --- 3. Start all LibreChat containers ---------------------------------
if podman ps -a --filter "name=librechat-app" --format '{{.Names}}' | grep -q librechat-app; then
echo "📦 Starting LibreChat containers..."
podman start librechat-app librechat-db librechat-admin 2>&1
else
echo "⚠️ Containers not found — creating via compose first..."
cd ~/librechat && podman-compose --in-pod true up -d \
|| { echo "❌ compose failed."; 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 is up."
break
fi
[ "$i" -eq 10 ] && { echo "❌ API down — last 30 log lines:"; podman logs --tail 30 librechat-app; exit 1; }
sleep 5
done
echo "=== $(date) done ==="
**Autostart setup (one-time, already done):**
* Nobara System Settings → Autostart → + Add New → Login Script → //~/librechat/boot-librechat.sh// → say Yes to make executable
* To disable: trash the entry, or //rm ~/librechat/boot-librechat.sh//
**Log file:** ///var/log/librechat-boot.log// — //tail -f /var/log/librechat-boot.log//
===== 7. Command Quick Reference =====
^ Action ^ Command ^
^ Start stack (manual) ^ podman start librechat-app librechat-db librechat-admin ^
^ Stop stack ^ podman stop librechat-app librechat-db librechat-admin ^
^ Restart containers after changing yaml/env ^ cd ~/librechat && podman-compose --in-pod true down && podman-compose --in-pod true up -d ^
^ Update LibreChat image (keeps all data) ^ cd ~/librechat && podman-compose --in-pod true down && podman pull ghcr.io/danny-avila/librechat:latest && podman-compose --in-pod true up -d ^
^ View app logs ^ podman logs --tail 100 librechat-app ^
^ View mongo logs ^ podman logs --tail 50 librechat-db ^
^ View panel logs ^ podman logs --tail 50 librechat-admin ^
^ View boot log ^ tail -f /var/log/librechat-boot.log ^
^ Pod status ^ podman pod ps ^
^ Remove stack, KEEP data ^ cd ~/librechat && podman-compose --in-pod true down ^
^ Nuke everything including data ^ above + podman volume rm librechat-db-data librechat-images librechat-uploads librechat-logs ^
^ List all users + roles ^ podman exec librechat-db mongosh --quiet --eval 'db.getSiblingDB("LibreChat").users.find({}, {email:1, roles:1}).toArray()' ^
^ Check LM Studio live models ^ curl -s http://localhost:1234/v1/models ^
**Notes:**
* "Restart after yaml/env change" = applies your config edits (this is the row you added — note correct hyphen in -in-pod)
* "Update image" additionally pulls the new release; afterwards re-extract index.html for the logo patch (section 5b)
* All down/rm operations never touch named volumes — data is safe
===== 8. Troubleshooting Log =====
==== 8a. Sign-in 500 "secretOrPrivateKey must have a value" ====
**Cause:** this image build signs refresh tokens with //process.env.JWT_REFRESH_SECRET//, not REFRESH_TOKEN_SECRET (confirmed at node_modules/@librechat/data-schemas/dist/index.mjs:5989). Registration worked because no token is issued at signup.
**Fix:** JWT_REFRESH_SECRET= in .env + recreate. ✅ Applied.
==== 8b. "Please select an image" prompt hangs scripts ====
**Cause:** bare image names trigger podman's interactive registry picker; without a TTY it blocks forever.
**Fix:** fully-qualified names in compose.yaml (docker.io/library/mongo:latest, ghcr.io/danny-avila/librechat:latest). ✅ Applied.
==== 8c. code-interpreter image 403 Forbidden ====
**Cause:** ghcr.io/danny-avila/code-interpreter:latest no longer exists in the registry; the official compose no longer ships it either [1].
**Fix:** service dropped — not needed for chat + ComfyUI MCP. ✅ Applied.
==== 8d. Logo too small on login page ====
**Cause:** built-in CSS sizes logo.svg fixed; no interface config key exists for it.
**Fix:** patched index.html → custom.css → img[src*="logo.svg"] override, 140px + centered. ✅ Verified in browser.
==== 8e. podman-compose --pod not recognized ====
**Cause:** the dnf package (v1.6.0) is containers/podman-compose v1.x — different flag than the pipx build.
**Fix:** use //--in-pod true// before the subcommand: podman-compose --in-pod true up -d. ✅ Applied.
==== 8f. "agent_id is required in request body" ====
**Cause:** message sent before the LM Studio endpoint was selected in the model picker — request routed to the agents endpoint.
**Fix:** new conversation, select model explicitly first. ✅ Resolved.
==== 8g. Admin panel image 403 on ghcr ====
**Cause:** ghcr.io/danny-avila/librechat-admin-panel:latest does not exist there; correct registry is registry.librechat.ai/librechat-ai/librechat-admin-panel:latest per the official compose [1].
**Fix:** corrected image reference. ✅ Container runs, panel serves at :3090.
==== 8h. Logs show "OpenID check failed: 500 .../openid/check" ====
**Cause:** known upstream bug — LibreChat issue #14504, fixed in PR #14507. Maintainer confirms it is log noise, NOT the login blocker (panel treats any non-ok response as "SSO unavailable" and falls back to email/password).
**Action:** none — ignore until an image update includes the fix.
==== 8i. Admin panel login: form submits, nothing happens ====
⚠️ **OPEN ITEM.** API logs show //[Login] [Login successful] [info@root3.de]// repeated — credentials accepted, auth works; the panel never establishes a browser session. Prime suspect: session cookies flagged secure over plain HTTP (candidate fixes: SESSION_COOKIE_SECURE=false / ADMIN_PANEL_SESSION_COOKIE_SECURE=false in .env) or panel-side session wiring. Secondary check pending: users.find({}, {email:1, roles:1}) to confirm exactly one user holds the ADMIN role (per #14504 thread, only the first registered user gets it).
**Status:** unresolved — next debugging step is the Network tab on :3090/login (failed request + status code) plus librechat-admin logs during an attempt.
===== 9. Status Overview =====
^ Component ^ Status ^ Note ^
^ Logo (big + centered) ^ ✅ verified in browser ^ custom.css, 140px ^
^ Auth: register + sign-in ^ ✅ working ^ JWT_REFRESH_SECRET fix ^
^ LM Studio chat ^ ✅ "Hello!" response confirmed ^ Qwen3 27B via fetch:true ^
^ ComfyUI MCP server ^ ✅ "running on stdio" in logs ^ comfyui-mcp package ^
^ Admin account ^ ✅ info@root3.de (first user = ADMIN) ^ panel is separate URL :3090 ^
^ Admin panel service ^ ✅ running, serves :3090 ^ login still open (8i) ^
^ Autostart (LLM first) ^ ✅ Nobara Login Script ^ /var/log/librechat-boot.log ^
^ Data persistence ^ ✅ named volumes ^ survives recreation/updates ^
^ Podman Desktop grouping ^ ✅ pod_librechat, 3 containers ^ --in-pod true ^
| --- | --- | --- |
^ ComfyUI image generation end-to-end ^ ⏳ not yet tested ^ last open functional test ^
======