LibreChat sandbox — Inference Co-op customization
Source of truth for the LibreChat evaluation instance at
librechat-test.inference.coop (Cloudron app 4023ccbf-d57a-4500-97df-14de9cf3516e,
appstore package ai.librechat.cloudronapp@1.4.1, which bundles LibreChat
v0.8.8 on image cloudron/ai.librechat.cloudronapp:202610150224320000).
This repo exists so the sandbox never drifts from what is deployed: the config
file here is byte-identical to /app/data/librechat.yaml, the MCP server here
is byte-identical to /app/data/mcp/server.py, and the Docker image is built
from this directory.
Architecture: what lives where
| Layer | Location | Survives image update? | Repo mirror |
|---|---|---|---|
| App config | /app/data/librechat.yaml (volume) |
yes | librechat.yaml |
| Env vars (env-file wins!) | /app/data/env (volume, sourced LAST by start.sh) |
yes | changed lines documented below |
| Env vars (Cloudron-managed) | cloudron env (process env) |
yes | documented below |
| MCP servers | /app/data/mcp/server.py + /app/data/mcp/image_server.py (volume) |
yes | mcp/ |
| Brand assets (logo, favicons) | baked into image overlay | no — rebuilt per image | assets/ |
| CloudronManifest.json | repo only (used by cloudron update) |
n/a | byte-identical copy of the OFFICIAL appstore manifest |
Precedence gotcha: start.sh does source /run/librechat/env AFTER the
process env, and /app/data/env is appended into it — so a value defined in
/app/data/env overrides the same cloudron env variable. That is why
ALLOW_EMAIL_LOGIN=false must be set in /app/data/env, not (only) via
cloudron env.
Deploy a config change (no image rebuild needed)
B64=$(base64 -w0 librechat.yaml)
cloudron --server my.inference.coop --token "$CLOUDRON_TOKEN" exec \
--app 4023ccbf-d57a-4500-97df-14de9cf3516e \
-- bash -c "echo '$B64' | base64 -d > /app/data/librechat.yaml"
cloudron --server my.inference.coop --token "$CLOUDRON_TOKEN" restart \
--app 4023ccbf-d57a-4500-97df-14de9cf3516e
Always diff the repo file against the live one after deploying. Always
deploy via base64 — heredocs and sed inserts have corrupted this file twice
(literal $(...) / bad indentation → LibreChat silently falls back to
defaults with only one log line).
Verify after every change:
cloudron logs --app <id> --lines 60 | grep -iE 'custom config|error|invalid|MCP'
curl -s https://librechat-test.inference.coop/api/config # appTitle, emailLoginEnabled, socialLogins
Build + deploy the image (brand assets only)
cd /tmp/librechat-custom # THIS dir: cloudron update reads CloudronManifest.json here
git add -A && git commit -m "..." && git push
SHA=$(git rev-parse --short HEAD)
cloudron builder build --repository registry.inference.coop/co-op/librechat-custom \
--tag 20261019-$SHA
cloudron --server my.inference.coop --token "$CLOUDRON_TOKEN" update \
--app 4023ccbf-d57a-4500-97df-14de9cf3516e \
--image registry.inference.coop/co-op/librechat-custom:20261019-$SHA
Rollback: rerun the update with the previous tag (or the appstore image
cloudron/ai.librechat.cloudronapp:<original-tag>).
NEVER edit CloudronManifest.json. cloudron update reads it from this
directory and applies it to the app. A hand-edited manifest that omits the
addons (postgresql, mongodb, oidc, sendmail, localstorage) strips
them from the running app and crash-loops it (mkdir: Read-only file system).
That is what broke the sandbox on 2026-10-18 — the manifest, not the overlay.
When the appstore package updates, refresh this file with a new copy pulled
from the Cloudron API (GET /api/v1/apps/<id> → .manifest) and bump the
FROM tag in the Dockerfile to the new manifest.dockerImage.
Env vars (live state)
Set in /app/data/env (volume — these WIN over cloudron env):
APP_TITLE="Inference Co-op"(was"LibreChat")ALLOW_EMAIL_LOGIN=false(wastrue) — password login removed; Cloudron SSO (OIDC) is the only login pathSCHEDULES_SINGLE_PROCESS=true— sandbox runs a single replica; silences the scheduler's Redis guard error
Set via cloudron env set (process env):
TTS_API_KEY/STT_API_KEY— dedicated LiteLLM virtual key (aliaslibrechat-speech, $5 / 30d budget, restricted totinfoil/voxtral-tts+tinfoil/whisper-large-v3-turbo). Regenerate with the gateway master key:POST /key/generate {"key_alias":"librechat-speech","models":[...],"max_budget":5,"budget_duration":"30d"}— never put the master key here.IMAGE_GEN_OAI_BASEURL=https://gateway.inference.coop/v1andIMAGE_GEN_OAI_MODEL=regolo/qwen-image— the agent "OpenAI Image Tools" points at the co-op gateway. NoIMAGE_GEN_OAI_API_KEY: members enter their own co-op key in the tool UI (per-member billing, same pattern as the chat endpoint'sapiKey: user_provided).SEARXNG_INSTANCE_URL=https://search.inference.coop— referenced from the YAML as${SEARXNG_INSTANCE_URL}(self-hosted search; keyless JSON API).
Feature notes (LibreChat v0.8.8)
- Chat models: custom endpoint "Inference Co-op" → gateway, per-member
keys, 8 chat modelSpecs (emoji convention shared with models.md).
regolo/qwen-imageis deliberately NOT in the chat picker (image models are not chat models; chat-calling it was the source of the old 500s). - Image generation — two paths:
- MCP tool (easiest):
inference-imageserver (toolgenerate_image) — members select it in the composer's + palette and just ask. No agent setup. First use: save your co-op key in the MCP panel (customUserVarsIC_API_KEY, injected per-user into the server process env;startup: falsebecause the key only exists per-user after they configure it). Bills to the member's own key (~1–2¢/image at 1024²). - Agent tool: LibreChat's built-in "OpenAI Image Tools" (env
IMAGE_GEN_OAI_BASEURL/IMAGE_GEN_OAI_MODEL→ gateway; no admin key, members paste their own). Note LibreChat v0.8.8 renders the generated image only inside the agent conversation.
- MCP tool (easiest):
- MCP:
inference-docs(toolsearch_docs, starts at boot) andinference-image(toolgenerate_image, per-user startup). Members may add their own via the MCP Settings panel ("bring your own tools"). Permissions seeded viainterface.mcpServers(use/create true, share false). - Speech: TTS
tinfoil/voxtral-tts(voicecasual_female), STTtinfoil/whisper-large-v3-turbo, both via the gateway with the capped key. - Web search: SearXNG (self-hosted) + keyless Keenable scraper, no reranker.
- Colors:
interface.themein the YAML is forward-compatible only — the theme engine ships in LibreChat releases AFTER 0.8.8 (merged to dev, not tagged). The block is silently ignored today and activates by itself when the appstore package bundles a newer LibreChat. No other runtime theming exists in 0.8.8.
Known state / limitations
- The
[schedules]Redis guard error is resolved bySCHEDULES_SINGLE_PROCESS=true(above). - The
RAG API not reachablewarning is expected (no rag_api addon); it only affects LibreChat's own file-RAG, which we do not use. - The
inference-imageMCP server logsTools: undefinedat boot — expected: withstartup: falseit registers but only spawns per-user after a member saves their key and reinitializes in the MCP panel.