Files
infra-librechat/k8s
Stéphane Tailland 2a695da0dd Declare the Grafana MCP server
Prometheus, Loki and Tempo become tools the assistant can call, so the
telemetry can be questioned rather than browsed — which is where the trace
dashboard kept running out of road. Verified: LibreChat initialises the
server in 168ms and loads its tool list.

No oauth block, unlike every other server here. The others sit behind a
Kong gateway enforcing openid-connect; this one is reached directly on the
cluster network and authenticates to Grafana with a service account token
of its own. LibreChat passes no user identity along, so there is no
per-user scoping: every LibreChat user gets the same full read of all
telemetry, including the Kong audit records and the LLM prompts now
captured there.

That is stated in the config rather than left to be discovered, and it is
the reason to move this server behind a gateway like the others.

allowedDomains gains the first http:// and first cluster-internal entry;
without it LibreChat's SSRF protection refuses the address.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 17:29:17 +02:00
..
2026-08-27 17:29:17 +02:00
2026-05-09 06:42:45 +00:00
2026-05-09 06:42:45 +00:00
2026-05-09 06:42:45 +00:00
2026-05-09 06:42:45 +00:00
2026-05-09 06:42:45 +00:00

LibreChat — Kubernetes Deployment

Deploys LibreChat on Kubernetes with MongoDB, MeiliSearch, and the JIRA MCP server.

Architecture

Namespace: librechat
│
├── Ingress nginx (librechat.sttlab.pc — TLS wildcard *.sttlab.pc)
│   └── librechat :3080
│       ├── mongodb :27017  (StatefulSet, 5 Gi PVC)
│       └── meilisearch :7700  (Deployment, 2 Gi PVC)
│
└── MCP JIRA (namespace: mcp)
    └── mcp-jira :9000  (streamable-http, 49 tools)
Component Image Storage
LibreChat ghcr.io/danny-avila/librechat:latest — (stateless)
MongoDB mongo:7 5 Gi PVC
MeiliSearch getmeili/meilisearch:v1.7 2 Gi PVC

Directory structure

k8s/
├── namespace.yaml             Namespace definition
├── secret.yaml                Credentials (gitignored — never commit real values)
├── configmap.yaml             Application environment config
├── kustomization.yaml         Kustomize entry point
├── mongodb/
│   ├── pvc.yaml
│   ├── statefulset.yaml
│   └── service.yaml
├── meilisearch/
│   ├── pvc.yaml
│   ├── deployment.yaml
│   └── service.yaml
├── librechat/
│   ├── configmap-app.yaml     Mounts librechat.yaml (MCP config, allowedDomains...)
│   ├── deployment.yaml
│   └── service.yaml
└── ingress/
    ├── ingress.yaml
    └── tls.yaml               TLS secret (gitignored — never commit)

Prerequisites

  • Kubernetes cluster with an Nginx ingress controller
  • A default StorageClass (for PVCs)
  • kubectl configured against your cluster
  • Wildcard TLS certificate for *.sttlab.pc in $HOME/tls/

Initial deployment

1. Fill in secrets

Edit secret.yaml with real values. Generate them with:

openssl rand -base64 24   # MONGO_PASSWORD
openssl rand -base64 32   # MEILI_MASTER_KEY, JWT_SECRET, JWT_REFRESH_SECRET
openssl rand -hex 32      # CREDS_KEY
openssl rand -hex 8       # CREDS_IV

Important: MONGO_URI must include ?authSource=admin because MONGO_INITDB_ROOT_USERNAME creates the user in the admin database:

mongodb://librechat:<password>@mongodb:27017/LibreChat?authSource=admin

2. Create the TLS secret

kubectl apply -f k8s/namespace.yaml

kubectl create secret tls sttlab-tls \
  --cert=$HOME/tls/sttlab.pc.crt \
  --key=$HOME/tls/sttlab.pc.key \
  --namespace=librechat \
  --dry-run=client -o yaml > k8s/ingress/tls.yaml

kubectl apply -f k8s/ingress/tls.yaml

3. Deploy

export DOMAIN=librechat.sttlab.pc
kubectl kustomize k8s/ | envsubst '${DOMAIN}' | kubectl apply -f -

Note: DOMAIN must be exported before the pipe. An inline assignment (DOMAIN=x cmd1 | cmd2) does not propagate to envsubst.

4. Verify

kubectl get pods,ingress -n librechat
curl -sk https://librechat.sttlab.pc/health

MCP JIRA

The JIRA MCP server runs in the mcp namespace (mcp-jira:9000, streamable-http transport).

librechat/configmap-app.yaml mounts /app/librechat.yaml into the container:

mcpSettings:
  allowedDomains:
    - "http://mcp-jira.mcp.svc.cluster.local:9000"
mcpServers:
  jira:
    type: streamable-http
    url: http://mcp-jira.mcp.svc.cluster.local:9000/mcp

mcpSettings.allowedDomains is required to allow internal k8s domains, which are blocked by default as SSRF protection.

Updating

export DOMAIN=librechat.sttlab.pc
kubectl kustomize k8s/ | envsubst '${DOMAIN}' | kubectl apply -f -

Force a pod restart:

kubectl rollout restart deployment/librechat -n librechat

Teardown

kubectl delete namespace librechat

This also deletes all PVCs and their data. Back up MongoDB first if needed.