> ## Documentation Index
> Fetch the complete documentation index at: https://docs.controlplane.com/llms.txt
> Use this file to discover all available pages before exploring further.

# LibreChat

> Deploy LibreChat on Control Plane. A ChatGPT-style chat UI for OpenAI, Anthropic, Google and OpenAI-compatible models, with MongoDB, conversation search, optional file chat and 1-4 replicas. Single location. Four secrets and an existing GVC must exist first.

## Overview

LibreChat is an open-source (MIT) ChatGPT-style chat interface that talks to OpenAI, Anthropic, Google and any OpenAI-compatible server (Ollama, vLLM, LiteLLM, OpenRouter) from one place, with per-user accounts, agents, presets and conversation search. This template deploys the LibreChat web UI and API on a standard workload together with a MongoDB database and a Meilisearch search index. Chat over your uploaded files and 2-4 replicas are optional.

The administrator account comes from a secret you create before installing, and self-service registration is off, so the UI is ready to sign in to on its public URL as soon as it starts. Model providers are optional: add API keys or point at your own model servers, or let each user bring a key.

> [!NOTE]
>
> This template does not create a GVC. You must deploy it into an existing GVC that you already have. The GVC must have exactly one location, because the datastores are single-instance and the uploaded-files volume exists per location.

### What Gets Created

| Resource | Name | Purpose | Optional |
| - | - | - | - |
| Workload | `RELEASE_NAME-librechat` | LibreChat web UI and API (standard, HTTP port `3080`, 1-4 replicas) | — |
| Volume Set | `RELEASE_NAME-librechat-files` | Shared volume at `/app/storage` for uploads, images and avatars, seen by every replica; no snapshots | — |
| Identity | `RELEASE_NAME-librechat-identity` | Identity bound to the workload | — |
| Policy | `RELEASE_NAME-librechat-policy` | Grants `reveal` on exactly the secrets the workload reads | — |
| Workload | `RELEASE_NAME-mongo` | MongoDB: chats, users, presets and settings (the [MongoDB](https://docs.controlplane.com/template-catalog/templates/mongodb.md) template) | — |
| Volume Set | `RELEASE_NAME-mongo-vs` | MongoDB data volume | — |
| Workload | `RELEASE_NAME-meilisearch` | Conversation search index (the [Meilisearch](https://docs.controlplane.com/template-catalog/templates/meilisearch.md) template) | `search.enabled: false` removes it |
| Volume Set | `RELEASE_NAME-meilisearch-data` | Meilisearch data volume | `search.enabled: false` removes it |
| Workload | `RELEASE_NAME-librechat-rag` | Document-search API, internal only (HTTP port `8000`) | `rag.enabled: true` |
| Workload | `RELEASE_NAME-pgvector` | Vector store for document embeddings (the [pgvector](https://docs.controlplane.com/template-catalog/templates/pgvector.md) template) | `rag.enabled: true` |
| Volume Set | `RELEASE_NAME-pgvector-vs` | pgvector data volume | `rag.enabled: true` |
| Workload | `RELEASE_NAME-redis` | Single Redis node for shared sessions, rate limits and resumable answers (the [Redis](https://docs.controlplane.com/template-catalog/templates/redis.md) template, dev mode) | `redis.enabled: true` |

Each datastore also creates its own identity and policy. The template creates **no credential of its own**: every key and password lives in a secret you create.

## Prerequisites

**Four secrets must exist before you install** (more when you turn on optional features). Secrets are org-level, so no GVC flag is involved. The MongoDB and Redis passwords go into connection strings, so use URL-safe values such as `openssl rand -hex 32`.

**1. Create the encryption and signing keys secret**

A [dictionary secret](https://docs.controlplane.com/guides/create-secret/dictionary.md) with exactly four keys. `CREDS_KEY` and `CREDS_IV` encrypt every API key your users save, so never change them; changing a `JWT_*` value signs everyone out:

```bash theme={null}
cpln secret create-dictionary --name KEYS_SECRET_NAME \
  --entry CREDS_KEY=$(openssl rand -hex 32) \
  --entry CREDS_IV=$(openssl rand -hex 16) \
  --entry JWT_SECRET=$(openssl rand -hex 32) \
  --entry JWT_REFRESH_SECRET=$(openssl rand -hex 32)
```

Set `keys.secretName` to this name.

**2. Create the admin account secret**

A dictionary secret with the keys `email` and `password` (8 or more characters). LibreChat creates this account on its first start, before the UI serves:

```bash theme={null}
cpln secret create-dictionary --name ADMIN_SECRET_NAME \
  --entry email=you@example.com \
  --entry password='YOUR-STRONG-PASSWORD'
```

Set `admin.secretName` to this name.

**3. Create the MongoDB credentials secret**

A dictionary secret with `username`, `password` and `database`:

```bash theme={null}
cpln secret create-dictionary --name MONGODB_SECRET_NAME \
  --entry username=librechat \
  --entry password=$(openssl rand -hex 32) \
  --entry database=LibreChat
```

Set `mongodb.config.credentialsSecretName` to this name.

**4. Create the Meilisearch key secret**

An [opaque secret](https://docs.controlplane.com/guides/create-secret/opaque.md) with `plain` encoding and a key of 16 or more bytes. Required while `search.enabled` is `true`:

```bash theme={null}
printf '%s' "$(openssl rand -hex 32)" | cpln secret create-opaque --name MEILI_SECRET_NAME --encoding plain -f -
```

Set `meilisearch.auth.secretName` to this name.

**5. Optional: create the model API key secrets**

One opaque secret per provider key, shared by every user, using the same `create-opaque` form with `--name PROVIDER_KEY_SECRET_NAME`. Reference them from `providers.openai.apiKeySecretName`, `providers.anthropic.apiKeySecretName`, `providers.google.apiKeySecretName`, a custom endpoint's `apiKeySecretName`, and `rag.embeddings.apiKeySecretName`. Local servers such as Ollama need no key.

**6. Optional: create the pgvector and Redis secrets**

For `rag.enabled: true`, a dictionary secret with `username`, `password` and `database`, set as `pgvector.config.credentialsSecretName`. For `redis.enabled: true`, a dictionary secret with the key `password`, set as `redis.redis.auth.fromSecret.name`:

```bash theme={null}
cpln secret create-dictionary --name PGVECTOR_SECRET_NAME \
  --entry username=librechat \
  --entry password=$(openssl rand -hex 32) \
  --entry database=librechat
cpln secret create-dictionary --name REDIS_SECRET_NAME \
  --entry password=$(openssl rand -hex 32)
```

> [!WARNING]
>
> **Create the secrets before installing, or the deployment wedges silently.** A name that points at a secret which does not exist installs "successfully" and then never starts, and `cpln logs` returns **zero lines**. The one place the reason appears is `status.versions[].message`:
>
> ```bash theme={null}
> cpln workload get-deployments RELEASE_NAME-librechat --gvc GVC_NAME -o yaml
> ```
>
> Use `get-deployments`; plain `cpln workload get` has no `versions` key. Once the secret exists the workload recovers on its own, or run `cpln workload force-redeployment RELEASE_NAME-librechat --gvc GVC_NAME` to skip the wait.

Backups of the database need a bucket and cloud access; see [Backing Up](#backing-up).

## Installation

Install from the marketplace registry with the four required values, the names of the secrets you created:

```bash theme={null}
cpln helm install RELEASE_NAME oci://ghcr.io/controlplane-com/templates/librechat \
  --version 1.0.0 --gvc GVC_NAME \
  --set keys.secretName=KEYS_SECRET_NAME \
  --set admin.secretName=ADMIN_SECRET_NAME \
  --set mongodb.config.credentialsSecretName=MONGODB_SECRET_NAME \
  --set meilisearch.auth.secretName=MEILI_SECRET_NAME
```

Then follow the instructions for your preferred method:

> [card] [UI](https://docs.controlplane.com/template-catalog/install-manage/ui.md)
>
> Browse, install, and manage templates visually

> [card] [CLI](https://docs.controlplane.com/template-catalog/install-manage/cli.md)
>
> Manage templates from your terminal

> [card] [Terraform](https://docs.controlplane.com/template-catalog/install-manage/terraform.md)
>
> Declare templates in your Terraform configurations

> [card] [Pulumi](https://docs.controlplane.com/template-catalog/install-manage/pulumi.md)
>
> Declare templates in your Pulumi programs

## Configuration

### Image and Core Settings

```yaml theme={null}
image: ghcr.io/librechat-ai/librechat:v0.8.8

resources:
  minCpu: 250m
  maxCpu: 1000m
  minMemory: 512Mi
  maxMemory: 2Gi

replicas: 1

files:
  volumeset:
    capacity: 10 # GiB (platform minimum 10); can be expanded later, never shrunk

keys:
  secretName: my-librechat-keys

admin:
  secretName: my-librechat-admin

auth:
  allowRegistration: false # true = anyone who reaches the URL can create an account (role USER)

mongodb:
  config:
    credentialsSecretName: my-librechat-mongodb-credentials
  resources:
    minCpu: 250m
    maxCpu: 1000m
    minMemory: 512Mi
    maxMemory: 1Gi
  volumeset:
    capacity: 10 # GiB
```

* `replicas` — 1 is the lighter shape; 2-4 keep chat up through restarts and upgrades and require `redis.enabled: true`. Document search runs the same count.
* `admin.secretName` — read on the first start only; later changes to the secret change nothing.
* Every other [MongoDB template](https://docs.controlplane.com/template-catalog/templates/mongodb.md) value works under `mongodb`, for example `mongodb.backup`.

### Model Providers

```yaml theme={null}
providers:
  openai:
    apiKeySecretName: ""    # e.g. my-librechat-openai-key
  anthropic:
    apiKeySecretName: ""    # e.g. my-librechat-anthropic-key
  google:
    apiKeySecretName: ""    # e.g. my-librechat-google-key
  userProvidedKeys: false   # true = providers above with no secret ask each user for their own key

customEndpoints: []
  # - name: ollama                                   # unique (any case); not a built-in: openAI, azureOpenAI, google, anthropic,
  #                                                  # assistants, azureAssistants, agents, custom, bedrock, gptPlugins, chatGPTBrowser
  #   baseURL: http://my-ollama-ollama.GVC_NAME.cpln.local:11434/v1
  #   apiKeySecretName: ""                           # opaque secret; "" = no key (local servers)
  #   models: [llama3.2]                             # shown even when fetchModels is false
  #   fetchModels: false                             # true = also list the server's /models
```

A provider with an empty `apiKeySecretName` is off. A custom endpoint is any OpenAI-compatible server, shown by name in the model menu; private addresses such as `*.cpln.local` work. Use the fully qualified `WORKLOAD_NAME.GVC_NAME.cpln.local` form for servers in the same GVC.

### Extra LibreChat Settings

```yaml theme={null}
config: {}
  # interface:
  #   customWelcome: "Welcome to our assistant"
  # registration:
  #   allowedDomains: [example.com]
```

`config` is merged over the generated `librechat.yaml` (lists replace). Never put an API key here: the file is part of the workload spec, so reference a key with `${VAR}`. See the [librechat.yaml reference](https://www.librechat.ai/docs/configuration/librechat_yaml).

### MCP Servers

MCP servers go in `config.mcpServers`. This example connects the Control Plane MCP server. Each user clicks **Connect** on it in the chat's MCP menu and signs in to Control Plane with OAuth, so no token is stored in the chart:

```yaml theme={null}
config:
  mcpServers:
    cpln:
      title: Control Plane
      type: streamable-http
      url: https://mcp.cpln.io/mcp?toolsets=full   # toolsets: readonly, core, mk8s or full
      requiresOAuth: true
      timeout: 60000
```

Every tool's description is sent with each message, and the `full` toolset has about 95 tools. If LibreChat reports that the context is exceeded, raise **Max Context Tokens** in the model's parameters, up to the model's limit.

### Search Files and Redis

```yaml theme={null}
search:
  enabled: true # false = no search box, and no Meilisearch workload

meilisearch:
  auth:
    secretName: my-librechat-meili-key
  resources:
    minCpu: 125m
    maxCpu: 500m
    minMemory: 256Mi
    maxMemory: 1Gi
  volumeset:
    capacity: 10 # GiB

rag:
  enabled: false # true = chat over uploaded files ("file search")
  image: ghcr.io/librechat-ai/librechat-rag-api-dev-lite:v0.9.0
  resources:
    minCpu: 100m
    maxCpu: 500m
    minMemory: 256Mi
    maxMemory: 1Gi
  embeddings:
    model: text-embedding-3-small # changing it after documents are indexed breaks their search
    baseUrl: "" # "" = OpenAI; any OpenAI-compatible URL, e.g. http://my-ollama-ollama.GVC_NAME.cpln.local:11434/v1
    apiKeySecretName: "" # opaque secret with the embeddings API key; required unless baseUrl is set

pgvector:
  config:
    credentialsSecretName: my-librechat-pgvector-credentials
  volumeset:
    capacity: 10 # GiB

redis:
  enabled: false
  mode: dev # LibreChat needs one plain Redis endpoint; the chart refuses production (Sentinel)
  redis:
    auth:
      fromSecret:
        enabled: true
        name: my-librechat-redis-password
        passwordKey: password
```

Every other `meilisearch`, `pgvector` and `redis` template value works under its key too.

### Access

```yaml theme={null}
publicAccess:
  enabled: true # chat UI on the canonical *.cpln.app URL; false = reach it with cpln port-forward

customDomain: "" # full URL, e.g. https://chat.example.com; sets the app's URL only — create the cpln domain yourself

internalAccess: # which workloads inside Control Plane may call LibreChat
  type: none # none | same-gvc | same-org | workload-list
  workloads: []
  # workloads:
  #   - //gvc/GVC_NAME/workload/WORKLOAD_NAME
```

## Connecting

| What | Address | Notes |
| - | - | - |
| Web UI | `status.canonicalEndpoint` of `RELEASE_NAME-librechat` | Public when `publicAccess.enabled: true` |
| Sign in | The `email` and `password` in your admin secret | Read them with `cpln secret reveal ADMIN_SECRET_NAME -o yaml` |
| Internal API | `http://RELEASE_NAME-librechat.GVC_NAME.cpln.local:3080` | Callers must be allowed by `internalAccess.type` |
| From your machine | `http://localhost:3080` | After the `port-forward` below |
| Custom endpoint in this GVC | `http://WORKLOAD_NAME.GVC_NAME.cpln.local:PORT/v1` | Always use the fully qualified name |

Read the public URL:

```bash theme={null}
cpln workload get RELEASE_NAME-librechat --gvc GVC_NAME -o yaml
```

When `publicAccess.enabled` is `false`, reach the UI through a tunnel:

```bash theme={null}
cpln port-forward RELEASE_NAME-librechat 3080:3080 --gvc GVC_NAME
```

### First Run

Open the web UI and sign in with the admin email and password. Add models through the providers or custom endpoints you configured, or ask users to enter their own keys when `providers.userProvidedKeys` is `true`. To use the Control Plane MCP server, set it up as shown in [MCP Servers](#mcp-servers) and have each user connect from the MCP menu.

## Operations

### Backing Up

Chats, users and settings live in MongoDB. Enable scheduled backups to object storage with the `mongodb.backup` values; the prerequisites (bucket, cloud account, access) and restore steps are on the [MongoDB](https://docs.controlplane.com/template-catalog/templates/mongodb.md) page. Uploaded files are not backed up: the shared files volume takes no snapshots, and uninstalling deletes every volume.

### Scaling and Availability

Set `replicas` to 2-4 and `redis.enabled` to `true` to keep chat available through restarts and upgrades. With Redis on, a browser that reconnects mid-answer picks the answer up on any replica; an answer is lost if the replica generating it crashes. The datastores are single-instance, so a MongoDB restart is a short outage. Answers are capped at 10 minutes per request.

### Troubleshooting

> [accordion] The deployment never becomes ready and the logs are empty
>
> A prerequisite secret is missing or misnamed. Run `cpln workload get-deployments RELEASE_NAME-librechat --gvc GVC_NAME -o yaml` and read `status.versions[].message`, then create the secret. See the warning under [Prerequisites](#prerequisites).

> [accordion] LibreChat reports that the context is exceeded when using MCP tools
>
> A large toolset such as `toolsets=full` sends many tool descriptions with every message. Raise **Max Context Tokens** in the model's parameters, up to the model's limit.

> [accordion] The install fails at render time
>
> The chart refuses `replicas` outside 1-4, more than 1 replica without `redis.enabled: true`, `redis.mode` other than `dev`, a plaintext Redis password, a custom endpoint named after a built-in one or with a duplicate name, a non-http(s) `baseURL` or no models, a `customDomain` with a path, and `rag.enabled` without `rag.embeddings.apiKeySecretName` or `rag.embeddings.baseUrl`. The error message names the value to fix.

## Important Notes

* **Never change `CREDS_KEY` or `CREDS_IV`.** Saved user API keys become unreadable.
* **The admin secret is read on first start only.** Afterwards change the password in the UI.
* **Never put an API key in `config`.** Reference it with `${VAR}`.
* **Decide on document search before indexing.** Changing `rag.embeddings.model` after files are indexed breaks their search.
* **Use one GVC location.** Extra locations duplicate single-instance datastores.
* **Uninstall deletes every volume,** including uploaded files.

## External References

* [LibreChat on GitHub](https://github.com/LibreChat-AI/LibreChat)
* [LibreChat documentation](https://www.librechat.ai/docs)
* [librechat.yaml reference](https://www.librechat.ai/docs/configuration/librechat_yaml)
* [MCP servers in librechat.yaml](https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/mcp_servers)
* [Control Plane MCP server](https://docs.controlplane.com/ai/mcp.md)
