Replies: 1 comment
|
Legend !! |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Working Self-Hosted Deployment: ClickHouse/code-interpreter + LibreChat v0.8.7
Validated: 2026-08-13
Status: Working end-to-end — stateful sessions, multi-file upload, matplotlib charts, and pytest all confirmed
Key repos:
A note of appreciation
None of this would exist without danny-avila and the LibreChat community. LibreChat is genuinely remarkable work — a unified, open-source AI interface that respects user privacy and gives you real control over your stack. The code interpreter integration, the agent framework, the JWT auth contract — all of it reflects careful, thoughtful engineering. Every time we looked at the code carefully enough, it was right. The gaps were in documentation, not implementation.
Thank you to Danny and everyone who contributed to LibreChat and to the
ClickHouse/code-interpreterproject.Why we're posting this
If you're searching for evidence of a successful self-hosted deployment of
ClickHouse/code-interpreterwith LibreChat — this is it. No issues. Stateful sessions, multi-file upload, matplotlib charts, and pytest all working.We couldn't find a single published account of anyone successfully doing this end to end. The repo has no deployment guide beyond
docker compose up --build, theCODEAPI_JWT_*variables are undocumented publicly. This guide shares everything we learned.Our setup: Ubuntu 24.04, no VM,
/dev/kvmon the host. One wrinkle: containerd defaulted to/var/lib/containerdon our root filesystem, which filled up mid-build. We moved it to/home/containerd— the guide covers this. After the base deployment was verified, we did a second build to addpytestto the packages available in the sandbox.Why it's so new: ClickHouse acquired LibreChat in late 2025. The paid LibreChat Code Interpreter API was discontinued and replaced with the open-source
ClickHouse/code-interpreter. LibreChat v0.8.7 (June 23, 2026) was the first release to officially ship the self-hosted interpreter. The repo was public for less than two months when this was written. The community is still catching up.This guide is designed to be pasted into an LLM and followed interactively — every step has an explicit gate so you know whether to proceed or stop.
Background: what you need to know before starting
The auth contract (read this first)
LibreChat v0.8.6+ (PR #13028) replaced
x-api-keywith minted EdDSA JWTs. The docs were not updated. You will see guides — including the current LibreChat docs page — that tell you to setLIBRECHAT_CODE_API_KEY. That variable is never read. LibreChat mints a short-lived Bearer token and sends it asAuthorization: Bearer <token>.ClickHouse/code-interpreterverifies these JWTs natively. If you setx-api-keyanywhere, it will be rejected with"Bearer token is required".Both sides are correct. The documentation is wrong.
How packages work (KVM/baked mode)
In the default KVM compose path, packages are baked into the Docker image at build time — not installed at runtime. The file that controls this is
docker/package-init.sh, which runs duringdocker buildinside thepackage-builderstage.build-packages.sh(repo root) — Kubernetes PVC mode only. Ignore it for local compose.docker/package-init.sh— the right file. Edit this to add packages.touch docker/package-init.shbefore rebuilding to bust Docker's layer cache — otherwise the build completes in seconds and your changes are silently absent.Network egress from the sandbox
pip installat session time is not possible, and this is a deliberate architectural decision rather than an oversight.The sandbox runs in its own network namespace (
clone_newnet: true). All outbound traffic is routed through the egress gateway, which is deny-by-default and only allows pre-approved internal destinations — the tool call server and file server. PyPI is not an approved destination and there is no config knob to add it. The design intent is that sandbox code cannot make arbitrary network calls, which is a meaningful part of the security model.The workaround — downloading wheels on the host and uploading them into the session — is fragile: Python 3.14 is new enough that many packages with C extensions don't have pre-built wheels yet, transitive dependencies must all be uploaded manually, and the install is session-scoped and doesn't survive to the next conversation. For anything you'll use regularly, bake it into the image.
A feature request for general configurable egress is tracked in issue #6, open as of this writing.
The
localhosttrapLIBRECHAT_CODE_BASEURLmust behttp://host.docker.internal:3112/v1, nothttp://localhost:3112/v1. Inside a container,localhostresolves to the container itself. With the wrong value the stack appears healthy but every agent execution silently fails with "Code execution is temporarily unavailable." Thesetup-local-auth-env.jsscript writeslocalhostby default — the runbook corrects this immediately after.Build times
docker/package-init.shdocker system prune(cache loss)Build times are faster than often cited (~10 min, not 60–90) because
uvhandles package installation and each compilation stage caches independently in Docker's layer cache.Agent persistence across rebuilds
Agents are stored in MongoDB (
librechat_librechat-datanamed volume), which is independent of the code-interpreter stack. Rebuilding or restarting the code-interpreter containers does not affect your agents.Always verify in the interpreter log, not the chat window
The model fabricates convincing execution output when execution fails. Always verify here:
A real execution produces
"Request received"and"Execution completed"log entries. If those aren't there, it didn't run.The config version warning
LibreChat will log
Outdated Config version: 1.2.8 / Latest version: 1.3.13on startup. This is cosmetic — the stack works with 1.2.8. Updateversion:inlibrechat.yamlwhen ready; it doesn't affect code interpreter functionality.System requirements
/dev/kvmmust exist —ls -l /dev/kvm/var/lib/containerdon/even if Docker's data root is elsewhere. If/is small (< 20 GB free), the build will fail mid-way with "no space left on device". Check both filesystems:If containerd is filling
/, move it before building:Runbook
Step 1 — Pre-flight checks
Gates:
/dev/kvmexists ✓ | 20+ GB free on Docker root ✓ | no container name collisions ✓ | no port collisions ✓Step 2 — Clone both repos
Gate: both files exist ✓
Step 3 — LibreChat base config
Generate secrets (run this and save the output):
Write them into
.env(replace<your-value>with each generated value):Verify:
grep -E '^(CREDS_KEY|CREDS_IV|JWT_SECRET|JWT_REFRESH_SECRET|SESSION_SECRET|SEARCH|ALLOW_REGISTRATION|OPENAI_API_KEY|UID|GID)=' .envGate: all 10 vars present and non-empty ✓
Step 4 — LibreChat config files
Write
librechat.yaml(adjust model names to match your API access):Write
docker-compose.override.yml:Fix directory ownership (UID/GID mismatch causes MongoDB to crash on startup):
Gate: both files exist ✓
Step 5 — Start LibreChat
Gate:
Server readiness checks passingin the api log ✓Go to
http://localhost:3080and create your account. Then close registration:sed -i 's/^ALLOW_REGISTRATION=.*/ALLOW_REGISTRATION=false/' .env docker compose up -d apiStep 6 — Generate JWT material
If node is missing:
sudo apt install -y nodejsExpected output:
Verify both sides match:
Gates:
CODEAPI_AUTH_PROVIDER=librechat-jwton both sides ✓ |kidvalue matches ✓ |x(public key) value matches in both JWK blobs ✓localhost, which doesn't work from inside a container:Gate:
LIBRECHAT_CODE_BASEURL=http://host.docker.internal:3112/v1✓Step 7 — Configure stateful sessions and production secrets
Gate: all three vars present and non-empty ✓
Step 8 — Build and start the code interpreter
First build takes ~10 minutes. Watch in a second terminal if you like:
When complete:
Gates: 8 containers up and healthy ✓ |
/v1/healthreturnsOK✓ | sandbox log showsBooting microVMandVMM seccomp filter applied✓KVM_ENABLED=false, nsjail is running against your host kernel with no VM boundary. Fix/dev/kvmaccess before proceeding.Step 9 — Auth handshake
Force-recreate the LibreChat api container to bake in the JWT env vars (plain
up -dis not enough — env is set at container creation):Gate: 9 CODEAPI vars listed ✓
Mint a real JWT and fire it at the interpreter:
Gate:
HTTP/1.1 200with"stdout":"4\n"✓If you get
Bearer token is required— the CODEAPI vars didn't reach the container. Check the override file and re-run--force-recreate.Step 10 — Agent execution in the UI
Creating the agent:
http://localhost:3080librechat.yamlmodel list to show up here.librechat.yamlcapabilities alone.Important — the model dropdown disappears once an agent is selected. After clicking Select, the top of the chat window will read "Message <agent name>" instead of showing a model picker. This is expected — the agent has its own model baked in. If you still see "Select a model" in the composer, the agent isn't active.
Test execution:
Send:
Execute this Python code and show me the output: print(2+2)Then verify it actually ran — don't trust the chat window alone:
Gate: log shows
"Request received"and"Execution completed"✓If the agent generates a code block but doesn't execute it, click the Run Code button on the code block. Both the agent-auto-execute path and the Run Code button path go through the same interpreter.
Step 11 — Stateful sessions
In a new conversation with the agent, send two separate messages:
Message 1:
Run this Python code: open('/mnt/data/probe.txt','w').write('one')Wait for it to complete, then:
Message 2:
Run this Python code: print(open('/mnt/data/probe.txt').read())Gate: Message 2 prints
one✓Step 12 — Multi-file upload and chart verification
Create two test CSVs on the host:
In LibreChat: click the paperclip → Upload to Code Environment → select both files → send:
Verify in the log:
Look for two upload events and
"files":{"count":2}on the execution requests."language":"bash"is expected — LibreChat routes Python throughbash_toolby design.Gate: chart renders inline; log shows two uploads and
"Execution completed"withcount:2✓Note: a broken "Revenue chart" text link sometimes appears below the rendered chart. This is a duplicate embed from the model — the actual chart is above it. Verify in the log, not the chat window.
Step 13 — Pytest verification (if baked in)
Send the agent:
can you run pytestExpected: "pytest ran successfully, but no tests were found — Collected: 0 tests." This is correct: pytest ran; there are just no test files in the sandbox.
Gate:
"Execution completed"in the log ✓To run a real test:
Write /mnt/data/test_math.py with a pytest test that asserts 2+2==4, then run pytest on it.Troubleshooting
Bearer token is required--force-recreateunknown_kidorbad_signaturesetup-local-auth-env.jsCode execution is temporarily unavailableLIBRECHAT_CODE_BASEURLtohost.docker.internal, force-recreateno space left on device/KVM_ENABLED=falsein sandbox log/dev/kvmnot accessiblekvmgroupOutdated Config versionin logslibrechat.yamlversion fieldversion:when ready.touch docker/package-init.shbefore rebuildingAdding Python packages
Edit
docker/package-init.sh— find the pip install block and add your package beforeopenpyxl:Rebuild —
touchis required to bust the Docker cache:Subsequent rebuilds take ~9 minutes. Python compilation is cached; only the uv package install reruns.
What we did next: adding pytest
After verifying the base deployment (Steps 1–13), we added
pytesttodocker/package-init.shand rebuilt in ~9 minutes. With stateful sessions, an agent can write a test file in one message and run pytest against it in the next — which is the actual use case for having it baked in.Key facts
mainbranch, 2026-08-13 — https://github.com/ClickHouse/code-interpreter/dev/kvmpresent, Docker root on/home/docker, containerd moved to/home/containerdCODEAPI_RUNTIME_SESSION_MODE=affinity+stateful_code_sessions: trueon the agentbash_tool)All reactions