User Tools

Site Tools


ai-librechat

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

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>

ai-librechat.txt · Last modified: by misa