This is an old revision of the document!
Table of Contents
Root³ Solutions LibreChat Deployment Guide
Stack: LibreChat + LM Studio (Qwen3 27B) + ComfyUI MCP Host: 192.168.1.203 — Nobara Linux, Podman (no Docker) Deployed: 2026-10-19 URL: http://192.168.1.203:3080
1. Folder Structure
All files live in one folder under the rootai home directory. No systemd units, no .env file required separately — everything is inline in compose.yaml or in the .env file mounted into the container.
~/librechat/ ├── compose.yaml # Podman Compose — 2 services (api + mongodb) ├── librechat.yaml # LibreChat config — endpoints, MCP, interface ├── boot-librechat.sh # Login autostart script (LLM-first ordering) ├── .env # Secrets + env vars (mounted to /app/.env) ├── root_solutions_logo.svg # Your logo — overwrites built-in logo.svg ├── index.html # Patched LibreChat index (links custom.css) └── custom.css # Logo size + centering override
2. compose.yaml (Full)
Two services only: the LibreChat API and MongoDB. Code-interpreter was dropped (ghcr image returns 403 — it has been removed from the registry [1]).
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 # Environment file with all secrets - ./.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" 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” on all services — you control startup via the boot script, no auto-restart on crash or reboot (matches your open-webui pattern)
- host.containers.internal:host-gateway — lets the container reach LM Studio and ComfyUI on the host machine by name instead of hardcoding a LAN IP [2]
- Named volumes for all durable data — safe to delete/recreate containers without losing chats, uploads, or the database [3]
- docker.io/library/mongo:latest fully qualified — prevents the interactive “select an image” prompt that hangs unattended scripts
3. .env File
All secrets and auth settings go here. Mounted to /app/.env inside the container. This is how LibreChat reads its config in this build (the @librechat/data-schemas package expects it there, not only in process env) [4].
# ~/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 # THIS IS THE ONE THAT ACTUALLY WORKS FOR SIGN-IN ON THIS IMAGE BUILD: JWT_REFRESH_SECRET=super_secret_refresh_token_string_54321
⚠️ Critical note on JWT_REFRESH_SECRET: This image build's @librechat/data-schemas package (line 5989 of dist/index.mjs) calls jwt.sign() with process.env.JWT_REFRESH_SECRET, not REFRESH_TOKEN_SECRET. Without this line, registration works but sign-in returns a 500 “secretOrPrivateKey must have a value” error. Both lines are kept for clarity; only JWT_REFRESH_SECRET is actually read by the signing code.
4. librechat.yaml (Full)
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 # pulls 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 package on npm (verified Oct 2026) - "--comfyui-url" - "http://host.containers.internal:8188"
Notes:
- baseURL is the correct field name (not “url”) [5]
- fetch: true means the model picker auto-populates from whatever is loaded in LM Studio — no need to hardcode every model ID [6]
- The original “mcp-server-comfyui” package does not exist on npm; comfyui-mcp is the real one, supports remote ComfyUI via –comfyui-url [7]
- First MCP call needs internet inside the container for npx to download the package — subsequent calls are cached
5. Logo Sizing (custom.css)
LibreChat renders logo.svg at a fixed small size in its built-in CSS and there is no config key to change it. The fix: patch index.html to load an extra stylesheet, then override the img rule [8].
5a. How it was set up
# 1) Copy the real index.html out of the container (while it's running) podman exec librechat-app cat /app/client/dist/index.html > ~/librechat/index.html # 2) Inject a link tag into <head> sed -i 's|</head>|<link rel="stylesheet" href="/custom.css"></head>|' ~/librechat/index.html # 3) Create the override 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 the size
Edit ~/librechat/custom.css on disk, change the px value, hard-refresh the browser (Ctrl+Shift+R). No container recreate needed — it's a bind mount, reads live.
6. boot-librechat.sh (Autostart Script)
Runs from Nobara Login Scripts (System Settings → Autostart → Login Scripts). Order matches your existing start-ai-new.sh pattern: LLM first, then 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 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 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/api/version > /dev/null \ || 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):
- Nobara System Settings → Autostart → + Add New → Login Script
- Select ~/librechat/boot-librechat.sh
- Say Yes when asked to make it executable
- To disable: trash the entry, or rm ~/librechat/boot-librechat.sh
Log file: /var/log/librechat-boot.log — check with tail -f /var/log/librechat-boot.log
7. Command Quick Reference
| Action | Command |
|---|---|
| Start stack (manual) | cd ~/librechat && podman start librechat-app librechat-db |
| Stop stack | podman stop librechat-app librechat-db |
| View logs (app) | podman logs –tail 100 librechat-app |
| View logs (mongo) | podman logs –tail 50 librechat-db |
| View boot log | tail -f /var/log/librechat-boot.log |
| Check pod status | podman pod ps |
| Update LibreChat image (keeps all data) | cd ~/librechat && podman-compose down && podman pull ghcr.io/danny-avila/librechat:latest && podman-compose –in-pod true up -d |
| Remove stack, keep data | cd ~/librechat && podman-compose down |
| Nuke everything including data | above + podman volume rm librechat-db-data librechat-images librechat-uploads librechat-logs |
| Query users in DB | podman exec librechat-db mongosh –quiet –eval 'db.getSiblingDB(“LibreChat”).users.findOne({}, {email:1, roles:1})' |
| Check LM Studio models live | curl -s http://localhost:1234/v1/models |
| up and down the container after updating ymal | podman-compose –in-pod true down && podman-compose –in-pod true up -d |
8. Troubleshooting Log
8a. Sign-in returns 500 "secretOrPrivateKey must have a value"
Cause: This LibreChat image build reads process.env.JWT_REFRESH_SECRET, not REFRESH_TOKEN_SECRET. The variable name is specific to the @librechat/data-schemas package version bundled in this image (confirmed at dist/index.mjs:5989). Fix: Add JWT_REFRESH_SECRET=<value> to ~/librechat/.env, then recreate.
8b. "Please select an image" prompt hangs the boot script
Cause: Bare image name (e.g. mongo:latest) triggers podman's interactive registry picker when no local match is found. With no TTY at login time this blocks forever. Fix: Always use fully qualified names in compose.yaml — docker.io/library/mongo:latest, ghcr.io/danny-avila/librechat:latest.
8c. Code-interpreter image returns 403 Forbidden
Cause: ghcr.io/danny-avila/code-interpreter:latest has been removed from the registry (403 = repo no longer public/exists). The current official LibreChat compose file does not include a code-interpreter service [9]. Fix: Drop the service entirely — it's not needed for chat + ComfyUI MCP.
8d. Logo too small on login page
Cause: LibreChat's built-in CSS sizes logo.svg to a fixed compact dimension. No config key exists in interface object structure [10]. Fix: Mount a patched index.html that links custom.css, then override with img[src*=“logo.svg”] { height: 140px !important; }.
8e. podman-compose --pod flag not recognised
Cause: Podman Desktop does not bundle podman-compose; the dnf package is v1.x (containers/podman-compose) [11], which uses a different flag name than the pipx version. Fix: Use podman-compose –in-pod true up -d instead of –pod.
8f. "agent_id is required in request body" on first chat
Cause: The conversation was created before the LM Studio endpoint was selected from the model picker, so the request routed to the agents endpoint. Fix: Start a new conversation and explicitly select LM Studio + model from the picker before sending.
9. What Works / What's Verified
| Component | Status | Notes |
|---|---|---|
| Logo (big + centered) | ✅ Verified in browser | custom.css mount, 140px |
| Auth (register + sign-in) | ✅ Working | JWT_REFRESH_SECRET fix applied |
| LM Studio chat | ✅ “Hello!” response confirmed | Qwen3 27B loaded |
| ComfyUI MCP server | ✅ “running on stdio” in logs | comfyui-mcp package |
| Admin access | ✅ First registered user is admin [1] | Role dropdown = viewer scope, not account role |
| Autostart (LLM first) | ✅ Nobara Login Script added | /var/log/librechat-boot.log |
| Data persistence | ✅ Named volumes | Survives container recreation |
| Podman Desktop grouping | ✅ pod_librechat, 2 containers | –in-pod true |
Not yet tested: ComfyUI image generation end-to-end from inside LibreChat (ask chat to generate an image).
</parameter> </function> </tool_call>
