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

> ## Agent Instructions
> These are the docs for Sevalla, a platform for deploying applications, databases, static sites, object storage and load balancers. The dashboard is at https://app.sevalla.com.
> Bold text in steps is an exact dashboard label.
> To act on a Sevalla account, prefer the Sevalla MCP server (https://mcp.sevalla.com/mcp, see https://docs.sevalla.com/ai-agents/overview.md), then the Sevalla CLI (https://docs.sevalla.com/developer-tools/cli.md), then the REST API (https://api-docs.sevalla.com, OpenAPI spec at https://api.sevalla.com/v3/openapi.json).
> Every page index is at https://docs.sevalla.com/llms.txt.

# Deploy LibreChat

> Run LibreChat from its official image with MongoDB and Meilisearch as private applications, Claude as the model, file uploads on a disk, and sign-up under your control.

LibreChat runs on Sevalla as three applications from official Docker images: `librechat/librechat` serves the chat interface and its API from the web process, MongoDB keeps users, conversations and file records, and Meilisearch, which you add for conversation search, indexes messages. MongoDB isn't a Sevalla managed database, so it runs as a private application with a disk, like Meilisearch: neither has HTTP traffic, and LibreChat reaches both over internal connections. Claude answers through your Anthropic API key, files that people attach are stored on LibreChat's disk, and only the accounts you create can sign in.

## At a glance

| | |
| - | - |
| Source | The `librechat/librechat:v0.8.8`, `mongo:8.0.34` and `getmeili/meilisearch:v1.35.1` Docker images |
| Start command | LibreChat: a custom start command that keeps uploaded images on the disk, then `exec npm run backend`. MongoDB and Meilisearch: the images' own |
| Port | LibreChat: `8080`, from the `PORT` environment variable that Sevalla sets (LibreChat's own default is `3080`). MongoDB: `27017`. Meilisearch: `7700`. MongoDB and Meilisearch have HTTP traffic turned off |
| Processes | One web process in each application |
| Services | `librechat-mongodb` and `librechat-meilisearch` applications over internal connections |
| Disk | 10 GB each: `/app/uploads` (LibreChat), `/data/db` (MongoDB), `/meili_data` (Meilisearch) |
| Health check | LibreChat: `GET /readyz` returns `OK` once it's ready (readiness probe). Meilisearch: `GET /health` (readiness probe) |
| Size | **S2** for LibreChat, **S1** for MongoDB and Meilisearch |

### Environment variables

These are the variables of the `librechat` application:

| Variable | Required | Available during | Secret | Value |
| - | - | - | - | - |
| `MONGO_URI` | Yes | Runtime | Yes | `mongodb://librechat:<password>@<MongoDB internal hostname>:27017/LibreChat?authSource=admin` |
| `DOMAIN_CLIENT` | Yes | Runtime | No | The application's URL, for example `https://librechat-x7k2m.sevalla.app` |
| `DOMAIN_SERVER` | Yes | Runtime | No | The same URL |
| `CREDS_KEY` | Yes | Runtime | Yes | Generate with `openssl rand -hex 32` |
| `CREDS_IV` | Yes | Runtime | Yes | Generate with `openssl rand -hex 16` |
| `JWT_SECRET` | Yes | Runtime | Yes | Generate with `openssl rand -hex 32` |
| `JWT_REFRESH_SECRET` | Yes | Runtime | Yes | Generate with `openssl rand -hex 32` |
| `ANTHROPIC_API_KEY` | Yes | Runtime | Yes | Your Anthropic API key |
| `ANTHROPIC_MODELS` | No | Runtime | No | `claude-haiku-5-5,claude-sonnet-5-5,claude-opus-5-5`. Without it, LibreChat lists every model your key can use |
| `ENDPOINTS` | No | Runtime | No | `anthropic`, so the model menu shows only Anthropic |
| `ALLOW_REGISTRATION` | Yes | Runtime | No | `false` |
| `TRUST_PROXY` | Yes | Runtime | No | `2` in Oracle Cloud Infrastructure (OCI) locations, `3` in Google Cloud locations |
| `SEARCH` | For search | Runtime | No | `true` |
| `MEILI_HOST` | For search | Runtime | No | `http://` plus Meilisearch's internal hostname plus `:7700` |
| `MEILI_MASTER_KEY` | For search | Runtime | Yes | The same key as on the `librechat-meilisearch` application |
| `PORT` | No | Runtime | No | Set by Sevalla to the web process port |

The `librechat-mongodb` application needs `MONGO_INITDB_ROOT_USERNAME` and `MONGO_INITDB_ROOT_PASSWORD`, and `librechat-meilisearch` needs `MEILI_MASTER_KEY`, `MEILI_ENV` and `MEILI_NO_ANALYTICS`: see [Run MongoDB as a private application](#run-mongodb-as-a-private-application) and [Add conversation search](#add-conversation-search). An application from a Docker image has no build, so every variable is a runtime variable.

## Before you begin

* A Sevalla company with a payment method: LibreChat needs the **S2** size.
* OpenSSL on your computer, to generate the passwords and keys (`openssl version` prints its version).
* An API key from [Anthropic](https://console.anthropic.com/). Set a spending limit in Anthropic's console: every account you create in LibreChat spends from this key.

## How LibreChat runs on Sevalla

| Application | Image | Why |
| - | - | - |
| `librechat` | `librechat/librechat:v0.8.8` | The chat interface and LibreChat's API, public on the application's domain. A disk at `/app/uploads` keeps the files people attach |
| `librechat-mongodb` | `mongo:8.0.34` | LibreChat's database, with users, conversations, messages and file records. The 8.0 line is the one LibreChat's own Docker Compose files use |
| `librechat-meilisearch` | `getmeili/meilisearch:v1.35.1` | The search index for conversations, the version LibreChat's Docker Compose files use. Optional |

Every image is pinned to a release, so a redeployment never pulls a newer version. A process with a [disk](https://docs.sevalla.com/applications/disks.md) runs 1 instance, and Sevalla stops it before it starts the new one on each deployment: in the tests, LibreChat answered `503` for about 15 seconds during a deployment, and MongoDB was back about 25 seconds after its deployment started.

## Run MongoDB as a private application

Generate the database password first. A hexadecimal password keeps characters that would break LibreChat's connection URL out of it:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
openssl rand -hex 24
```

**1. Create the application**

In the sidebar, click **Applications**, then click **Create** > **Application**. Under **Source**, click **Docker image** and enter `mongo:8.0.34`. Keep **None (public image)** as the **Registry credential**. Enter `librechat-mongodb` as the **Name**, choose a **Data center** and the **S1** size, then click **Create application**.

**2. Add the variables**

Click the **Environment variables** tab, then **Add environment variables**. Add `MONGO_INITDB_ROOT_USERNAME` with the value `librechat`, and `MONGO_INITDB_ROOT_PASSWORD` with the password you generated, then click **Add environment variables**.

**3. Set the port and turn HTTP traffic off**

Click the **Networking** tab. In **Expose HTTP traffic**, click **Update port**, enter `27017` and click **Update port**. Then click **Disable**, and **Disable HTTP** in the dialog.

**4. Add the disk**

Click the **Disks** tab, then **Create disk**. Keep the web process, enter `/data/db` as the **Path**, choose **10 GB** and click **Create disk**.

**5. Deploy**

Click the **Overview** tab, then click **Deploy now** under **Recent deployments**, and **Deploy**.

| Setting | Why |
| - | - |
| `MONGO_INITDB_ROOT_USERNAME` and `MONGO_INITDB_ROOT_PASSWORD` | On the first start, with an empty disk, the image creates this user and turns on authentication. Without them, MongoDB accepts connections without a password. Changing them later has no effect: the user already exists in the data on the disk |
| Port `27017` | MongoDB's port. Other services reach the web process port on the application's internal hostname |
| HTTP traffic off | The application's domains stop answering, so nobody on the internet reaches the database. It stays reachable on the private network |
| Disk at `/data/db` | MongoDB's data folder. Without a disk, every deployment starts with an empty database: no users, no conversations |

MongoDB has no HTTP endpoint for a readiness probe, so the deployment finishes when the container starts. The [runtime logs](https://docs.sevalla.com/applications/logs.md) show `Waiting for connections` when it accepts connections. To check the user, open the application's **Web terminal** tab and run:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
mongosh --quiet -u "$MONGO_INITDB_ROOT_USERNAME" -p "$MONGO_INITDB_ROOT_PASSWORD" --authenticationDatabase admin --eval "db.version()"
```

It prints `8.0.34`. The **Networking** tab lists the web process's internal hostname, for example `librechat-mongodb-k3j4h-web.librechat-mongodb-k3j4h.svc.cluster.local`: LibreChat's `MONGO_URI` needs it.

## Deploy LibreChat

Generate the keys on your computer, and store them in your password manager:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
echo "CREDS_KEY=$(openssl rand -hex 32)"
echo "CREDS_IV=$(openssl rand -hex 16)"
echo "JWT_SECRET=$(openssl rand -hex 32)"
echo "JWT_REFRESH_SECRET=$(openssl rand -hex 32)"
```

**1. Create the application**

Click **Applications**, then click **Create** > **Application**. Under **Source**, click **Docker image** and enter `librechat/librechat:v0.8.8`. Keep **None (public image)** as the **Registry credential**. Enter `librechat` as the **Name**, choose MongoDB's **Data center** and the **S2** size, then click **Create application**. Don't deploy yet. Note the domain under the name, for example `librechat-x7k2m.sevalla.app`.

**2. Add the disk**

Click the **Disks** tab, then **Create disk**. Keep the web process, enter `/app/uploads` as the **Path**, choose **10 GB** and click **Create disk**.

**3. Connect MongoDB**

Click the **Networking** tab. Under **Connected services**, click **Add internal connection**, choose `librechat-mongodb` as the **Service**, and click **Add internal connection**.

**4. Add the environment variables**

Click the **Environment variables** tab, then click **Add environment variables**. Paste the lines that follow these steps into the first key field, replace the values with yours, and click **Add environment variables**.

**5. Set the start command and the readiness probe**

Click the **Processes** tab, click **⋯** on the web process, then click **Update process**. Enter the **Custom start command** that follows these steps. Under **Health checks**, turn on **Readiness probe**, enter the **Path** `/readyz`, the **Initial delay** `10` and the **Timeout** `5`, keep the **Port** at `8080`, then click **Update process**.

**6. Deploy**

Click the **Overview** tab, click **Deploy now**, then click **Deploy**.

```bash .env theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
MONGO_URI=mongodb://librechat:<password>@librechat-mongodb-k3j4h-web.librechat-mongodb-k3j4h.svc.cluster.local:27017/LibreChat?authSource=admin
DOMAIN_CLIENT=https://librechat-x7k2m.sevalla.app
DOMAIN_SERVER=https://librechat-x7k2m.sevalla.app
CREDS_KEY=<64 hexadecimal characters>
CREDS_IV=<32 hexadecimal characters>
JWT_SECRET=<64 hexadecimal characters>
JWT_REFRESH_SECRET=<64 hexadecimal characters>
ANTHROPIC_API_KEY=sk-ant-your-key
ANTHROPIC_MODELS=claude-haiku-5-5,claude-sonnet-5-5,claude-opus-5-5
ENDPOINTS=anthropic
ALLOW_REGISTRATION=false
TRUST_PROXY=2
```

```bash Custom start command theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
mkdir -p /app/uploads/images && rmdir /app/client/public/images && ln -s /app/uploads/images /app/client/public/images && exec npm run backend
```

| Setting | Why LibreChat needs it on Sevalla |
| - | - |
| `MONGO_URI` | The database, with MongoDB's password and internal hostname. The image creates the user in MongoDB's `admin` database, and `authSource=admin` tells LibreChat to sign in there |
| `DOMAIN_CLIENT`, `DOMAIN_SERVER` | The public URL LibreChat builds links and sign-in cookies from. With an `https://` URL, the cookies are marked secure |
| `CREDS_KEY`, `CREDS_IV` | Encrypt the API keys and credentials that people save in LibreChat. `CREDS_KEY` must be exactly 64 hexadecimal characters and `CREDS_IV` 32 |
| `JWT_SECRET`, `JWT_REFRESH_SECRET` | Sign the sign-in tokens. Without these four variables, LibreChat generates values into `/app/.env.temp`, a file that the next deployment deletes, so every deployment signs everyone out and saved credentials can no longer be read |
| `ANTHROPIC_API_KEY` | The key every chat uses. LibreChat's own default, `user_provided`, makes each person paste their own key |
| `ANTHROPIC_MODELS`, `ENDPOINTS` | Limit the model menu to Anthropic and to these models. The first model is the default for new chats |
| `ALLOW_REGISTRATION` | `false` hides **Sign up** and answers `403` with `{"message":"Registration is not allowed."}`, so only the accounts you create can sign in. LibreChat's default, `true`, lets anyone who finds the domain create an account and chat on your key |
| `TRUST_PROXY` | How many proxies forward each request: Cloudflare and Sevalla's ingress in OCI locations, one more in Google Cloud locations ([which locations use which cloud](https://docs.sevalla.com/platform/locations.md)). LibreChat limits sign-in attempts and messages per visitor address, and finds that address by skipping these proxies |
| Custom start command | LibreChat saves uploaded documents in `/app/uploads` and uploaded images in `/app/client/public/images`, and a process has one disk. The command points the images folder at `/app/uploads/images` on the disk, then starts LibreChat as the image does. `exec` replaces the shell, so LibreChat receives `SIGTERM` and logs `Received SIGTERM, draining HTTP server...` and `Graceful shutdown complete, exiting` during a deployment |
| Readiness probe | `/readyz` answers `OK` only once LibreChat has connected to MongoDB and loaded its configuration, so requests keep going to the running version during a deployment |

In the tests, LibreChat used about 1 GB of memory right after it started (987 MB on **S1**, 1,031 MB on **S2**) and about 650 MB once idle. **S1** has 1 GB, so use **S2** (1 vCPU, 2 GB). MongoDB used up to 362 MB and Meilisearch up to 147 MB on **S1**.

## Create the admin account

With `ALLOW_REGISTRATION=false`, LibreChat has no sign-up page, so you create accounts with its `create-user` script. The first account becomes the admin.

**1. Open the web terminal**

On the `librechat` application, click the **Web terminal** tab.

**2. Create the account**

Run the script with an email address, a display name and a username:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
npm run create-user -- admin@example.com Admin admin
```

Enter a password when the script asks for it, and `y` when it asks whether the email is verified. It prints `User created successfully!`.

## Verify the deployment

1. On the **Deployments** tab, the deployment's status is **Deployed**. With the API, [Get deployment](https://api-docs.sevalla.com/v3/applications/get-deployment) (`getDeploymentV3`) returns `"status": "success"`.

2. The [runtime logs](https://docs.sevalla.com/applications/logs.md) show `Connected to MongoDB` and `Server listening on all interfaces at port 8080. Use http://localhost:8080 to access it`.

3. The readiness endpoint answers:

   ```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
   curl -s https://librechat-x7k2m.sevalla.app/readyz
   ```

   ```text theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
   OK
   ```

4. Sign-up is off:

   ```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
   curl -s -X POST https://librechat-x7k2m.sevalla.app/api/auth/register \
     -H "Content-Type: application/json" \
     -d '{"name":"Eve","username":"eve","email":"eve@example.com","password":"correct-horse-battery","confirm_password":"correct-horse-battery"}'
   ```

   ```json theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
   {"message":"Registration is not allowed."}
   ```

5. Open the domain. LibreChat shows **Welcome back** with no sign-up link. Sign in with the admin account: the model menu at the top shows `claude-haiku-5-5`.

The runtime logs also show `Config file YAML format is invalid: ENOENT: no such file or directory, open '/app/librechat.yaml'` at every start. LibreChat runs on its defaults without that file; [Deploy with a Dockerfile](#deploy-with-a-dockerfile) adds one.

## Chat with Claude

**1. Turn off thinking**

In the left sidebar, click **Parameters** and turn off **Thinking**.

**2. Send a message**

Type a question in **Message Claude** and press Enter. The answer appears word by word as Claude writes it.

LibreChat 0.8.8 turns **Thinking** on by default and sends it to `claude-haiku-5-5` in the format older models use, which this model rejects. With **Thinking** on, the answer is an error that contains `"thinking.type.enabled" is not supported for this model. Use "thinking.type.adaptive" and "output_config.effort" to control thinking behavior.` To reuse the setting in later chats, click **Save As Preset**. To make **Thinking** off the default for everyone, use the `librechat.yaml` in [Deploy with a Dockerfile](#deploy-with-a-dockerfile). `claude-sonnet-5-5` and `claude-opus-5-5` work with **Thinking** on, and cost more per message.

Answers stream to the browser as Server-Sent Events: LibreChat's stream endpoint answers with `Content-Type: text/event-stream` and `Cache-Control: no-cache, no-transform`, and Cloudflare and Sevalla's proxy pass the events through without buffering. In the test, a five-sentence answer arrived in 78 pieces, the first 1.3 s after the message was sent and the last after 2.6 s.

## Upload files

Click **Attach Files** (the paperclip) in the message box, choose **Upload to Provider**, and pick a PDF or an image. LibreChat sends the file to Claude with your message, and Claude answers from it. In the test, Claude answered a question about a one-page PDF with the right figures and a citation of the line it used.

LibreChat stores each file on the disk: documents in `/app/uploads/<user ID>/` and images in `/app/uploads/images/<user ID>/`, through the start command's link. It also keeps them for the rest of the conversation: after redeploying both LibreChat and MongoDB, a follow-up question in the same conversation got an answer from the PDF again. Without the disk, files uploaded before a deployment are gone.

## Let other people sign in

Keep `ALLOW_REGISTRATION=false` and create an account for each person in the **Web terminal**, the same way as the admin's:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
npm run create-user -- dana@example.com Dana dana
```

Accounts after the first have the **User** role. To let people create their own accounts instead, set `ALLOW_REGISTRATION` to `true` and deploy: **Sign up** appears on the sign-in page, and anyone who can open the domain can create an account. Turn it off again when everyone has one.

As the admin, you choose what the **User** role can do: in the left sidebar, open **Prompts** or **Memories**, click **Admin Settings**, choose the **Role** **User**, change the switches, and click **Save**.

## Keep the data

Users, conversations and file records are in MongoDB on its disk; uploaded files are on LibreChat's disk. In the tests, both survived a redeployment of each application, and LibreChat reconnected to MongoDB on its own.

Sevalla [backs up disks](https://docs.sevalla.com/applications/disks.md#backups) every day. For a copy you can restore elsewhere, export the database in the `librechat-mongodb` application's **Web terminal**:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
mongodump -u "$MONGO_INITDB_ROOT_USERNAME" -p "$MONGO_INITDB_ROOT_PASSWORD" --authenticationDatabase admin --db LibreChat --archive=/data/db/librechat.archive
```

The archive is on the disk at `/data/db/librechat.archive`.

## Add conversation search

Search in the conversation list needs Meilisearch. It runs as one more private application with a disk.

Generate its master key:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
openssl rand -hex 32
```

**1. Create the application**

Click **Applications**, then **Create** > **Application**. Under **Source**, click **Docker image** and enter `getmeili/meilisearch:v1.35.1`. Enter `librechat-meilisearch` as the **Name**, choose the same **Data center** and the **S1** size, and click **Create application**.

**2. Add the variables**

On the **Environment variables** tab, add `MEILI_MASTER_KEY` with the key you generated, `MEILI_ENV` with `production`, and `MEILI_NO_ANALYTICS` with `true`.

**3. Set the port and turn HTTP traffic off**

On the **Networking** tab, in **Expose HTTP traffic**, click **Update port**, enter `7700` and click **Update port**. Then click **Disable**, and **Disable HTTP**.

**4. Add the disk and the probe**

On the **Disks** tab, create a **10 GB** disk with the **Path** `/meili_data`. On the **Processes** tab, update the web process: turn on **Readiness probe** with the **Path** `/health` and the **Port** `7700`.

**5. Deploy**

Click the **Overview** tab, click **Deploy now**, then click **Deploy**.

**6. Connect LibreChat**

On the `librechat` application's **Networking** tab, add an internal connection to `librechat-meilisearch`. On its **Environment variables** tab, add `SEARCH` with `true`, `MEILI_HOST` with `http://` plus Meilisearch's internal hostname plus `:7700`, for example `http://librechat-meilisearch-r8t2n-web.librechat-meilisearch-r8t2n.svc.cluster.local:7700`, and `MEILI_MASTER_KEY` with the same key. Then deploy `librechat`.

Meilisearch logs `Environment:		"production"` and `A master key has been set. Requests to Meilisearch won't be authorized unless you provide an authentication key.` when it starts. After LibreChat's next start, its runtime logs show `[syncWithMeili] Completed sync for conversations.`: LibreChat indexes the conversations that existed before search was on, then each new message. The search box appears above the conversation list.

## Deploy with a Dockerfile

LibreChat needs no build: the official image runs as it is. A Dockerfile is for settings that only LibreChat's configuration file, `librechat.yaml`, can change, such as a default model with **Thinking** off for everyone, and it moves the images link into the image, so the web process needs no custom start command. Add these files to the root of a repository:

```dockerfile Dockerfile theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
FROM librechat/librechat:v0.8.8

# Uploaded images go to the disk at /app/uploads, next to uploaded documents
RUN rmdir /app/client/public/images \
 && ln -s /app/uploads/images /app/client/public/images

COPY librechat.yaml /app/librechat.yaml

CMD ["sh", "-c", "mkdir -p /app/uploads/images && exec npm run backend"]
```

```yaml librechat.yaml theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
version: 1.3.17
cache: true

modelSpecs:
  enforce: true
  list:
    - name: claude-haiku-5-5
      label: Claude Haiku 5.5
      description: Fast and low cost
      default: true
      preset:
        endpoint: anthropic
        model: claude-haiku-5-5
        thinking: false
```

```text .dockerignore theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
.git
.env*
*.log
```

| Line or choice | Why |
| - | - |
| `FROM librechat/librechat:v0.8.8` | The official image, pinned to the release this guide uses. The image runs as the unprivileged `node` user (UID 1000), and the Dockerfile keeps it |
| `RUN rmdir ... && ln -s ...` | The images folder becomes a link to `/app/uploads/images` on the disk, built once into the image |
| `COPY librechat.yaml` | Root owns the file, so LibreChat can read it but not change it |
| `CMD` | Creates the images folder on the disk, then starts LibreChat as the image does. `exec` lets LibreChat receive `SIGTERM` |
| `modelSpecs` with `enforce: true` | Everyone chats through this one model spec, with **Thinking** off, so `claude-haiku-5-5` works without changing **Parameters**. Add a spec per model you want to offer |

Commit and push the three files. Then point the `librechat` application at the repository:

**1. Change the source**

On the application's **Settings** tab, in the **Source** section, click **Update source**. Click **Private repository**, choose the Git provider and the repository, and click **Update source**.

**2. Choose the Dockerfile build strategy**

In the **Build strategy** section, click **Update build strategy**. Click **Dockerfile**, keep the **Dockerfile path** `Dockerfile` and the **Docker context** `.`, then click **Update build strategy**.

**3. Remove the custom start command**

On the **Processes** tab, click **⋯** on the web process, then **Update process**. Clear the **Custom start command**, so the image's `CMD` runs, and click **Update process**.

**4. Deploy**

Click the **Overview** tab, click **Deploy now**, pick the branch, then click **Deploy**.

The build log shows `Building and pushing docker image using Dockerfile...`, and the deployment's **Dockerfile** tab shows the file. In the tests, a deployment from the Dockerfile took 2 to 4 minutes, most of it pulling the 980 MB base image; after a change to `librechat.yaml`, the `RUN` layer came from the cache. The runtime logs no longer show the `ENOENT` line for `librechat.yaml`, and the model menu shows **Claude Haiku 5.5**. The checks in [Verify the deployment](#verify-the-deployment) answer the same way.

## Upgrade LibreChat

1. Read the release notes of every version between yours and the new one on [LibreChat's GitHub releases](https://github.com/danny-avila/LibreChat/releases), including the `librechat.yaml` version changes.
2. Back up the database with `mongodump`, as in [Keep the data](#keep-the-data).
3. On the `librechat` application's **Settings** tab, in the **Source** section, click **Update source**, change the tag in **Docker image** (or the `FROM` line of your `Dockerfile`), and save.
4. Deploy.

Upgrade MongoDB and Meilisearch only to versions that LibreChat's Docker Compose files use, and back up MongoDB first. Meilisearch holds only an index: if you replace its disk, LibreChat indexes the conversations again at its next start.

## Add a custom domain

To serve LibreChat from your own domain, such as `chat.example.com`, add it on the `librechat` application's **Domains** tab and create the DNS records Sevalla shows; the web process must be larger than **H1**, which **S2** is. Then change `DOMAIN_CLIENT` and `DOMAIN_SERVER` to `https://chat.example.com` and deploy. See [Custom domains](https://docs.sevalla.com/domains/overview.md).

## Troubleshoot

| Symptom | Cause | Fix |
| - | - | - |
| The answer is an error that contains `"thinking.type.enabled" is not supported for this model.` | **Thinking** is on for `claude-haiku-5-5` | Turn off **Thinking** in **Parameters**, or use the `librechat.yaml` model spec from [Deploy with a Dockerfile](#deploy-with-a-dockerfile) |
| `curl` calls to LibreChat's chat API answer `event: error` with `{"message":"Illegal request"}` | LibreChat accepts chat requests only with a browser's `User-Agent` header | Send the request from a browser, or with a browser `User-Agent` |
| The log shows `[credentials] CREDS_KEY must be exactly 64 hexadecimal characters (32 bytes).` and LibreChat stops | `CREDS_KEY` has the wrong length or format | Generate it with `openssl rand -hex 32`. If it protects existing data, put the original back |
| Everyone is signed out after each deployment | `JWT_SECRET` and `JWT_REFRESH_SECRET` aren't set, so LibreChat generates new ones into `/app/.env.temp` | Set all four keys from [Deploy LibreChat](#deploy-librechat) and deploy |
| Users and conversations are gone after MongoDB's deployment | `librechat-mongodb` has no disk at `/data/db` | Add the disk. Data from before is lost |
| An uploaded image doesn't load after a deployment | The custom start command isn't set, so images were written outside the disk | Set the start command from [Deploy LibreChat](#deploy-librechat). Images uploaded before are lost |
| A sign-up request returns `403` with `{"message":"Registration is not allowed."}` | `ALLOW_REGISTRATION` is `false` | Create the account with `create-user`, or see [Let other people sign in](#let-other-people-sign-in) |
| The log shows `Config file YAML format is invalid: ENOENT: no such file or directory, open '/app/librechat.yaml'` | The image has no `librechat.yaml` | Nothing to fix: LibreChat runs on its defaults. To add one, see [Deploy with a Dockerfile](#deploy-with-a-dockerfile) |

## Deploy with the API

These calls create the same deployment without the dashboard: MongoDB, LibreChat with its disk, variables, start command and readiness probe, the first deployment and the admin account. They need:

* An [API key](https://docs.sevalla.com/developer-tools/api-keys.md) that can create applications, in `SEVALLA_API_KEY`, and your Anthropic key in `ANTHROPIC_API_KEY`.
* `curl`, `jq` and `openssl`.

The images are public, so no Git provider needs to be connected. The steps create 2 applications, 3 with search; the API allows 20 service creations per hour per company.

**1. Choose the location and sizes**

[List clusters](https://api-docs.sevalla.com/v3/resources/list-clusters) (`listClustersV3`), [List process resource types](https://api-docs.sevalla.com/v3/resources/list-process-resource-types) (`listProcessResourceTypesV3`) and [List disk resource types](https://api-docs.sevalla.com/v3/resources/list-disk-resource-types) (`listDiskResourceTypesV3`). Pick the location by its region ID, here Frankfurt:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
API=https://api.sevalla.com/v3
AUTH="Authorization: Bearer $SEVALLA_API_KEY"
CLUSTER_ID=$(curl -s "$API/resources/clusters" -H "$AUTH" | jq -r '.[] | select(.location == "eu-frankfurt-1") | .id')
S1=$(curl -s "$API/resources/process-resource-types" -H "$AUTH" | jq -r '.[] | select(.name == "s1") | .id')
S2=$(curl -s "$API/resources/process-resource-types" -H "$AUTH" | jq -r '.[] | select(.name == "s2") | .id')
DISK_10GB=$(curl -s "$API/resources/disk-resource-types" -H "$AUTH" | jq -r '.[] | select(.size_gib == 10) | .id')
```

CLI: `sevalla resources clusters`, `sevalla resources process-types` and `sevalla resources disk-types`. MCP: `get_platform_options`, and `sevalla_api_read` with `listDiskResourceTypesV3`.

**2. Create the MongoDB application**

[Create application](https://api-docs.sevalla.com/v3/applications/create-application) (`createApplicationV3`) with the Docker image source, then [List processes](https://api-docs.sevalla.com/v3/applications/list-processes) (`listProcessesV3`) for its web process and internal hostname. The API creates the web process at **S1**:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
MONGO_APP_ID=$(curl -s -X POST "$API/applications" -H "$AUTH" -H "Content-Type: application/json" \
  -d "$(jq -n --arg c "$CLUSTER_ID" '{display_name:"librechat-mongodb", cluster_id:$c, source:"dockerImage", docker_image:"mongo:8.0.34"}')" \
  | jq -r .id)
MONGO_WEB_ID=$(curl -s "$API/applications/$MONGO_APP_ID/processes" -H "$AUTH" | jq -r '.data[] | select(.type == "web") | .id')
MONGO_HOST=$(curl -s "$API/applications/$MONGO_APP_ID/processes" -H "$AUTH" | jq -r '.data[] | select(.type == "web") | .internal_hostname')
```

CLI: `sevalla apps create --name librechat-mongodb --cluster $CLUSTER_ID --source dockerImage --docker-image mongo:8.0.34`. MCP: `create_application` with `deploy: false`, then `get_application`.

**3. Set the port, turn HTTP off and add the disk**

[Update process](https://api-docs.sevalla.com/v3/applications/update-process) (`updateProcessV3`); `is_ingress_enabled: false` is the dashboard's **Disable HTTP**. Then [Attach or update process disk](https://api-docs.sevalla.com/v3/applications/attach-or-update-process-disk) (`updateProcessDiskV3`):

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
curl -s -X PATCH "$API/applications/$MONGO_APP_ID/processes/$MONGO_WEB_ID" -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"port": 27017, "is_ingress_enabled": false}' | jq -c '{port, is_ingress_enabled}'
curl -s -X PUT "$API/applications/$MONGO_APP_ID/processes/$MONGO_WEB_ID/disk" -H "$AUTH" -H "Content-Type: application/json" \
  -d "$(jq -n --arg r "$DISK_10GB" '{resource_type_id:$r, mount_path:"/data/db"}')" | jq -r .mount_path
```

```text theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
{"port":27017,"is_ingress_enabled":false}
/data/db
```

CLI: `sevalla apps processes disk update $MONGO_WEB_ID --app-id $MONGO_APP_ID --resource-type $DISK_10GB --mount-path /data/db`; the CLI can't set the port or turn HTTP off. MCP: `sevalla_api_update` with `updateProcessV3` and `updateProcessDiskV3`.

**4. Add MongoDB's variables**

[Create environment variable](https://api-docs.sevalla.com/v3/applications/create-environment-variable) (`createEnvironmentVariableV3`), once per variable. Keep `MONGO_PASSWORD`: LibreChat's connection URL needs it:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
MONGO_PASSWORD=$(openssl rand -hex 24)
add_var() {
  curl -s -X POST "$API/applications/$1/env-vars" -H "$AUTH" -H "Content-Type: application/json" \
    -d "$(jq -n --arg k "$2" --arg v "$3" '{key:$k, value:$v, is_runtime:true, is_buildtime:false}')" | jq -r .key
}
add_var "$MONGO_APP_ID" MONGO_INITDB_ROOT_USERNAME librechat
add_var "$MONGO_APP_ID" MONGO_INITDB_ROOT_PASSWORD "$MONGO_PASSWORD"
```

Each call prints the key it added. CLI: `sevalla apps env-vars create $MONGO_APP_ID --key MONGO_INITDB_ROOT_USERNAME --value librechat`. MCP: `set_environment_variables`.

**5. Deploy MongoDB**

[Trigger deployment](https://api-docs.sevalla.com/v3/applications/trigger-deployment) (`triggerDeploymentV3`), then [Get deployment](https://api-docs.sevalla.com/v3/applications/get-deployment) (`getDeploymentV3`) until `status` is final (`success`, `failed`, `cancelled` or `skipped`), for up to 30 minutes. The `deploy` function deploys LibreChat later too:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
deploy() {
  DEPLOYMENT_ID=$(curl -s -X POST "$API/applications/$1/deployments" -H "$AUTH" -H "Content-Type: application/json" -d '{}' | jq -r .id)
  for i in $(seq 1 120); do
    STATUS=$(curl -s "https://api.sevalla.com/v3/applications/$1/deployments/$DEPLOYMENT_ID" \
      -H "Authorization: Bearer $SEVALLA_API_KEY" | jq -r '.status // empty')
    case $STATUS in success|failed|cancelled|skipped) break ;; esac
    sleep 15
  done
  echo "$STATUS"
}
deploy "$MONGO_APP_ID"
```

```text theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
success
```

If it fails, [Get application deployment logs](https://api-docs.sevalla.com/v3/applications/get-application-deployment-logs) (`getApplicationDeploymentLogsV3`) with `?limit=5000` returns the whole log. CLI: `sevalla apps deployments trigger $MONGO_APP_ID`, then `sevalla apps deployments get $DEPLOYMENT_ID --app-id $MONGO_APP_ID`. MCP: `trigger_deployment`, then `get_deployment`.

**6. Check MongoDB**

[Run a command in a process](https://api-docs.sevalla.com/v3/applications/run-a-command-in-a-process) (`execProcessCommandV3`) runs `mongosh` in the container with the image's variables. MongoDB has no readiness probe, so the deployment can finish a few seconds before it accepts connections: on the first start, the image restarts MongoDB once after it creates the user. The loop tries for up to 1 minute:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
for i in $(seq 1 12); do
  MONGO_VERSION=$(curl -s -X POST "$API/applications/$MONGO_APP_ID/processes/$MONGO_WEB_ID/exec" -H "$AUTH" -H "Content-Type: application/json" \
    -d '{"command": ["sh", "-c", "mongosh --quiet -u \"$MONGO_INITDB_ROOT_USERNAME\" -p \"$MONGO_INITDB_ROOT_PASSWORD\" --authenticationDatabase admin --eval \"db.version()\""]}' \
    | jq -r '.stdout // empty')
  [ -n "$MONGO_VERSION" ] && break
  sleep 5
done
echo "$MONGO_VERSION"
```

```text theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
8.0.34
```

CLI: `sevalla apps processes exec $MONGO_WEB_ID --app-id $MONGO_APP_ID -- sh -c '...'`. MCP: `sevalla_api_create` with `execProcessCommandV3`.

**7. Create the LibreChat application**

[Create application](https://api-docs.sevalla.com/v3/applications/create-application) (`createApplicationV3`), then [List application domains](https://api-docs.sevalla.com/v3/applications/list-application-domains) (`listApplicationDomainsV3`) and [List processes](https://api-docs.sevalla.com/v3/applications/list-processes) (`listProcessesV3`):

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
APP_ID=$(curl -s -X POST "$API/applications" -H "$AUTH" -H "Content-Type: application/json" \
  -d "$(jq -n --arg c "$CLUSTER_ID" '{display_name:"librechat", cluster_id:$c, source:"dockerImage", docker_image:"librechat/librechat:v0.8.8"}')" \
  | jq -r .id)
for i in $(seq 1 30); do APP_HOST=$(curl -s "$API/applications/$APP_ID/domains" -H "$AUTH" | jq -r '.data[] | select(.is_primary) | .name'); [ -n "$APP_HOST" ] && break; sleep 2; done
WEB_ID=$(curl -s "$API/applications/$APP_ID/processes" -H "$AUTH" | jq -r '.data[] | select(.type == "web") | .id')
```

CLI: `sevalla apps create --name librechat --cluster $CLUSTER_ID --source dockerImage --docker-image librechat/librechat:v0.8.8`. MCP: `create_application` with `deploy: false`, then `get_application`.

**8. Set the size, start command and readiness probe**

[Update process](https://api-docs.sevalla.com/v3/applications/update-process) (`updateProcessV3`). The API creates the web process at **S1**, which is too small. Always send `timeoutSeconds`: without it, the probe gets 120 seconds instead of the dashboard's 1:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
curl -s -X PATCH "$API/applications/$APP_ID/processes/$WEB_ID" -H "$AUTH" -H "Content-Type: application/json" \
  -d "$(jq -n --arg r "$S2" '{resource_type_id:$r, entrypoint:"mkdir -p /app/uploads/images && rmdir /app/client/public/images && ln -s /app/uploads/images /app/client/public/images && exec npm run backend", readiness_probe:{httpGet:{path:"/readyz", port:8080}, initialDelaySeconds:10, periodSeconds:5, timeoutSeconds:5, successThreshold:1, failureThreshold:3}}')" \
  | jq -r .resource_type_name
```

```text theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
s2
```

CLI: `sevalla apps processes update $WEB_ID --app-id $APP_ID --resource-type $S2 --command "<the start command>"` (the CLI can't set a probe). MCP: `sevalla_api_update` with `updateProcessV3` and the same body.

**9. Add the disk and connect MongoDB**

[Attach or update process disk](https://api-docs.sevalla.com/v3/applications/attach-or-update-process-disk) (`updateProcessDiskV3`) and [Create internal connection](https://api-docs.sevalla.com/v3/applications/create-internal-connection) (`createApplicationInternalConnectionV3`), with `target_type` `app` for an application:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
curl -s -X PUT "$API/applications/$APP_ID/processes/$WEB_ID/disk" -H "$AUTH" -H "Content-Type: application/json" \
  -d "$(jq -n --arg r "$DISK_10GB" '{resource_type_id:$r, mount_path:"/app/uploads"}')" | jq -r .mount_path
curl -s -X POST "$API/applications/$APP_ID/internal-connections" -H "$AUTH" -H "Content-Type: application/json" \
  -d "$(jq -n --arg t "$MONGO_APP_ID" '{target_id:$t, target_type:"app"}')" | jq -r 'has("id")'
```

```text theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
/app/uploads
true
```

CLI: `sevalla apps processes disk update $WEB_ID --app-id $APP_ID --resource-type $DISK_10GB --mount-path /app/uploads` and `sevalla apps internal-connections create $APP_ID --target-id $MONGO_APP_ID --target-type app`. MCP: `sevalla_api_update` with `updateProcessDiskV3`, and `sevalla_api_create` with `createApplicationInternalConnectionV3`.

**10. Add LibreChat's variables**

[Create environment variable](https://api-docs.sevalla.com/v3/applications/create-environment-variable) (`createEnvironmentVariableV3`), with the `add_var` function from before. Store the four generated keys somewhere safe if you need them again; they stay readable on the **Environment variables** tab:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
add_var "$APP_ID" MONGO_URI "mongodb://librechat:$MONGO_PASSWORD@$MONGO_HOST:27017/LibreChat?authSource=admin"
add_var "$APP_ID" DOMAIN_CLIENT "https://$APP_HOST"
add_var "$APP_ID" DOMAIN_SERVER "https://$APP_HOST"
add_var "$APP_ID" CREDS_KEY "$(openssl rand -hex 32)"
add_var "$APP_ID" CREDS_IV "$(openssl rand -hex 16)"
add_var "$APP_ID" JWT_SECRET "$(openssl rand -hex 32)"
add_var "$APP_ID" JWT_REFRESH_SECRET "$(openssl rand -hex 32)"
add_var "$APP_ID" ANTHROPIC_API_KEY "$ANTHROPIC_API_KEY"
add_var "$APP_ID" ANTHROPIC_MODELS claude-haiku-5-5,claude-sonnet-5-5,claude-opus-5-5
add_var "$APP_ID" ENDPOINTS anthropic
add_var "$APP_ID" ALLOW_REGISTRATION false
add_var "$APP_ID" TRUST_PROXY 2
```

`TRUST_PROXY` is `2` in OCI locations such as Frankfurt; use `3` in Google Cloud locations. CLI: `sevalla apps env-vars create $APP_ID --key ENDPOINTS --value anthropic`, once per variable; the CLI makes each variable available during runtime and build, which makes no difference for an image. MCP: `set_environment_variables`.

**11. Deploy LibreChat**

The same `deploy` function, with [Trigger deployment](https://api-docs.sevalla.com/v3/applications/trigger-deployment) (`triggerDeploymentV3`) and [Get deployment](https://api-docs.sevalla.com/v3/applications/get-deployment) (`getDeploymentV3`):

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
deploy "$APP_ID"
```

```text theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
success
```

CLI: `sevalla apps deployments trigger $APP_ID`. MCP: `trigger_deployment`, then `get_deployment`.

**12. Create the admin account**

[Run a command in a process](https://api-docs.sevalla.com/v3/applications/run-a-command-in-a-process) (`execProcessCommandV3`) runs LibreChat's `create-user` script. The password is part of the command, and the script warns `password passed in as argument, this is not secure!`; keep `ADMIN_PASSWORD`:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
ADMIN_PASSWORD=$(openssl rand -hex 12)
curl -s -X POST "$API/applications/$APP_ID/processes/$WEB_ID/exec" -H "$AUTH" -H "Content-Type: application/json" \
  -d "$(jq -n --arg p "$ADMIN_PASSWORD" '{command:["npm","run","create-user","--","admin@example.com","Admin","admin",$p,"--email-verified=true"], timeout:60}')" \
  | jq -r .stdout | grep -o 'User created successfully!'
```

```text theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
User created successfully!
```

CLI: `sevalla apps processes exec $WEB_ID --app-id $APP_ID -- npm run create-user -- admin@example.com Admin admin "$ADMIN_PASSWORD" --email-verified=true`. MCP: `sevalla_api_create` with `execProcessCommandV3`.

**13. Check LibreChat**

The readiness endpoint answers, the admin can sign in through LibreChat's API, the model list holds the three Claude models, and sign-up is off:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
curl -s -w "\n" "https://$APP_HOST/readyz"
LOGIN=$(curl -s -X POST "https://$APP_HOST/api/auth/login" -H "Content-Type: application/json" \
  -d "$(jq -n --arg p "$ADMIN_PASSWORD" '{email:"admin@example.com", password:$p}')")
echo "$LOGIN" | jq -r .user.role
TOKEN=$(echo "$LOGIN" | jq -r .token)
curl -s "https://$APP_HOST/api/models" -H "Authorization: Bearer $TOKEN" | jq -c .anthropic
curl -s -w " %{http_code}\n" -X POST "https://$APP_HOST/api/auth/register" -H "Content-Type: application/json" \
  -d '{"name":"Eve","username":"eve","email":"eve@example.com","password":"correct-horse-battery","confirm_password":"correct-horse-battery"}'
```

```text theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
OK
ADMIN
["claude-haiku-5-5","claude-sonnet-5-5","claude-opus-5-5"]
{"message":"Registration is not allowed."} 403
```

CLI: `sevalla apps logs runtime $APP_ID --search "Connected to MongoDB"`. MCP: `search_runtime_logs`.

Then open `https://$APP_HOST`, sign in as `admin@example.com` with `ADMIN_PASSWORD`, and [chat with Claude](#chat-with-claude).

### Add search with the API

**1. Create Meilisearch**

[Create application](https://api-docs.sevalla.com/v3/applications/create-application) (`createApplicationV3`), then [List processes](https://api-docs.sevalla.com/v3/applications/list-processes) (`listProcessesV3`):

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
MEILI_APP_ID=$(curl -s -X POST "$API/applications" -H "$AUTH" -H "Content-Type: application/json" \
  -d "$(jq -n --arg c "$CLUSTER_ID" '{display_name:"librechat-meilisearch", cluster_id:$c, source:"dockerImage", docker_image:"getmeili/meilisearch:v1.35.1"}')" \
  | jq -r .id)
MEILI_WEB_ID=$(curl -s "$API/applications/$MEILI_APP_ID/processes" -H "$AUTH" | jq -r '.data[] | select(.type == "web") | .id')
MEILI_HOST=$(curl -s "$API/applications/$MEILI_APP_ID/processes" -H "$AUTH" | jq -r '.data[] | select(.type == "web") | .internal_hostname')
```

CLI: `sevalla apps create --name librechat-meilisearch --cluster $CLUSTER_ID --source dockerImage --docker-image getmeili/meilisearch:v1.35.1`. MCP: `create_application` with `deploy: false`.

**2. Configure and deploy it**

[Update process](https://api-docs.sevalla.com/v3/applications/update-process) (`updateProcessV3`), [Attach or update process disk](https://api-docs.sevalla.com/v3/applications/attach-or-update-process-disk) (`updateProcessDiskV3`), [Create environment variable](https://api-docs.sevalla.com/v3/applications/create-environment-variable) (`createEnvironmentVariableV3`), then the `deploy` function:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
curl -s -X PATCH "$API/applications/$MEILI_APP_ID/processes/$MEILI_WEB_ID" -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"port": 7700, "is_ingress_enabled": false, "readiness_probe": {"httpGet": {"path": "/health", "port": 7700}, "initialDelaySeconds": 5, "periodSeconds": 5, "timeoutSeconds": 2, "successThreshold": 1, "failureThreshold": 3}}' \
  | jq -c '{port, is_ingress_enabled}'
curl -s -X PUT "$API/applications/$MEILI_APP_ID/processes/$MEILI_WEB_ID/disk" -H "$AUTH" -H "Content-Type: application/json" \
  -d "$(jq -n --arg r "$DISK_10GB" '{resource_type_id:$r, mount_path:"/meili_data"}')" | jq -r .mount_path
MEILI_MASTER_KEY=$(openssl rand -hex 32)
add_var "$MEILI_APP_ID" MEILI_MASTER_KEY "$MEILI_MASTER_KEY"
add_var "$MEILI_APP_ID" MEILI_ENV production
add_var "$MEILI_APP_ID" MEILI_NO_ANALYTICS true
deploy "$MEILI_APP_ID"
```

```text theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
{"port":7700,"is_ingress_enabled":false}
/meili_data
MEILI_MASTER_KEY
MEILI_ENV
MEILI_NO_ANALYTICS
success
```

CLI: `sevalla apps env-vars create` once per variable, then `sevalla apps deployments trigger $MEILI_APP_ID`. MCP: `sevalla_api_update`, `set_environment_variables` and `trigger_deployment`.

**3. Connect LibreChat and deploy it**

[Create internal connection](https://api-docs.sevalla.com/v3/applications/create-internal-connection) (`createApplicationInternalConnectionV3`), the three search variables, and a deployment of LibreChat. [Get application runtime logs](https://api-docs.sevalla.com/v3/applications/get-application-runtime-logs) (`getApplicationRuntimeLogsV3`) shows the first sync, and LibreChat's API reports search as on:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
curl -s -X POST "$API/applications/$APP_ID/internal-connections" -H "$AUTH" -H "Content-Type: application/json" \
  -d "$(jq -n --arg t "$MEILI_APP_ID" '{target_id:$t, target_type:"app"}')" | jq -r 'has("id")'
add_var "$APP_ID" SEARCH true
add_var "$APP_ID" MEILI_HOST "http://$MEILI_HOST:7700"
add_var "$APP_ID" MEILI_MASTER_KEY "$MEILI_MASTER_KEY"
deploy "$APP_ID"
TOKEN=$(curl -s -X POST "https://$APP_HOST/api/auth/login" -H "Content-Type: application/json" \
  -d "$(jq -n --arg p "$ADMIN_PASSWORD" '{email:"admin@example.com", password:$p}')" | jq -r .token)
curl -s -w "\n" "https://$APP_HOST/api/search/enable" -H "Authorization: Bearer $TOKEN"
```

```text theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
true
SEARCH
MEILI_HOST
MEILI_MASTER_KEY
success
true
```

CLI: `sevalla apps internal-connections create $APP_ID --target-id $MEILI_APP_ID --target-type app`. MCP: `sevalla_api_create` with `createApplicationInternalConnectionV3`.

### Build from the Dockerfile with the API

With the `Dockerfile`, `librechat.yaml` and `.dockerignore` from [Deploy with a Dockerfile](#deploy-with-a-dockerfile) pushed to a repository on a connected Git provider, [Update application](https://api-docs.sevalla.com/v3/applications/update-application) (`updateApplicationV3`) switches the source and the build strategy, and [Update process](https://api-docs.sevalla.com/v3/applications/update-process) (`updateProcessV3`) clears the custom start command so the image's `CMD` runs:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
curl -s -X PATCH "$API/applications/$APP_ID" -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"source": "privateGit", "git_type": "github", "repo_url": "https://github.com/acme/librechat", "build_type": "dockerfile", "dockerfile_path": "Dockerfile", "docker_context": "."}' \
  | jq -c '{source, build_type}'
curl -s -X PATCH "$API/applications/$APP_ID" -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"default_branch": "main"}' | jq -r .default_branch
curl -s -X PATCH "$API/applications/$APP_ID/processes/$WEB_ID" -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"entrypoint": ""}' | jq -c '{entrypoint}'
deploy "$APP_ID"
```

```text theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
{"source":"privateGit","build_type":"dockerfile"}
main
{"entrypoint":""}
success
```

Send `default_branch` in its own call: in the tests, it was ignored in the same call that changed the source. Without access to the repository, the first call answers `400` with `Cannot access repository`. CLI: `sevalla apps update $APP_ID --data '{"build_type":"dockerfile","dockerfile_path":"Dockerfile","docker_context":"."}'`. MCP: `sevalla_api_update` with `updateApplicationV3`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.