> ## 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 Open WebUI

> Deploy Open WebUI from its official image with PostgreSQL and pgvector, a disk for uploads and a first admin account, chatting with LiteLLM and Ollama models over internal connections.

Open WebUI runs on Sevalla as a public application: the official image serves the chat interface from the web process, keeps users, chats, settings and document vectors in a Sevalla PostgreSQL database with pgvector, stores uploaded files on a disk, and reaches its models over internal connections: [LiteLLM](https://docs.sevalla.com/guides/ai/litellm.md) for chat, with one virtual key and budget for the whole interface, and [Ollama](https://docs.sevalla.com/guides/ai/ollama.md) for document embeddings. Answers stream to the browser over a WebSocket through Sevalla's proxy, with no extra settings.

## At a glance

| | |
| - | - |
| Source | The `ghcr.io/open-webui/open-webui:v0.12.5-slim` Docker image |
| Build | None: Sevalla pulls the image |
| Start command | The image's own, `bash start.sh` |
| Port | `8080`, the image's default |
| Processes | Web process |
| Services | PostgreSQL 18 with pgvector over an internal connection; [LiteLLM](https://docs.sevalla.com/guides/ai/litellm.md) and [Ollama](https://docs.sevalla.com/guides/ai/ollama.md) over internal connections |
| Disk | 10 GB at `/app/backend/data`, on the web process |
| Health check | `GET /health` returns `200` (readiness probe) |
| Size | **S1** |

### Environment variables

| Variable | Required | Available during | Secret | Value |
| - | - | - | - | - |
| `DATABASE_URL` | Yes | Runtime | Yes | `DB_URL` from the internal connection, renamed |
| `WEBUI_SECRET_KEY` | Yes | Runtime | Yes | Generate with `openssl rand -hex 32` |
| `WEBUI_ADMIN_EMAIL` | Yes | Runtime | No | The admin's email address |
| `WEBUI_ADMIN_PASSWORD` | Yes | Runtime | Yes | A strong password. Used only while there are no users |
| `OPENAI_API_BASE_URL` | Yes | Runtime | No | `http://` plus LiteLLM's internal hostname plus `:8080/v1` |
| `OPENAI_API_KEY` | Yes | Runtime | Yes | The virtual key for Open WebUI from LiteLLM |
| `OLLAMA_BASE_URL` | Yes | Runtime | No | `http://` plus Ollama's internal hostname plus `:8080` |
| `ENABLE_OLLAMA_API` | No | Runtime | No | `False` |
| `RAG_EMBEDDING_ENGINE` | Yes | Runtime | No | `ollama` |
| `RAG_EMBEDDING_MODEL` | Yes | Runtime | No | `embeddinggemma-2:270m` |
| `PORT` | No | Runtime | No | Set by Sevalla to the web process port |

These variables fill in Open WebUI's settings on the first start only. See [Change settings after the first start](#change-settings-after-the-first-start).

## Before you begin

* A Sevalla company with a payment method.
* A [LiteLLM proxy](https://docs.sevalla.com/guides/ai/litellm.md) with a [virtual key for Open WebUI](https://docs.sevalla.com/guides/ai/litellm.md#create-a-key-for-each-application), and an [Ollama application](https://docs.sevalla.com/guides/ai/ollama.md) with `embeddinggemma-2:270m` pulled, both in the location where Open WebUI will run.

## How Open WebUI runs on Sevalla

![Open WebUI runs as the only public app, with a disk for uploads and its own PostgreSQL database with pgvector. It sends chats to a LiteLLM proxy over an internal connection, and document embeddings to Ollama. LiteLLM, an application with HTTP turned off and its own PostgreSQL database for keys and spend, routes requests to model providers such as Anthropic and OpenAI outside Sevalla and, over an internal connection, to Ollama, an application with HTTP turned off that runs on CPU only and keeps its models on a disk.](https://assets.sevalla.com/brand/diagrams/docs/self-hosted-llm-stack-light.svg)

| Part | Setup |
| - | - |
| Image | `ghcr.io/open-webui/open-webui:v0.12.5-slim`, pinned to a release. The slim variant is a 196 MB download instead of 1.6 GB: it leaves out the embedding, speech and reranking models the standard image runs locally, and stores document vectors in PostgreSQL with pgvector. This setup gets its models from LiteLLM and Ollama, so it needs none of them |
| Port | `8080`, the image's default and the web process port |
| Database | Sevalla PostgreSQL with the pgvector extension, from `DATABASE_URL` |
| Files | A disk at `/app/backend/data`. With PostgreSQL, Open WebUI writes only uploaded files (`uploads/`) and caches (`cache/`) there |
| Settings | Environment variables fill in the settings on the first start. After that, the settings live in the database, and you change them in Open WebUI's **Admin Panel** |
| Size | **S1** (0.5 vCPU, 1 GB). Memory peaked at 424 MB in the tests |
| Instances | 1, because the process has a disk |

## Create the database

**1. Open the create form**

In the sidebar, click **Databases**, then click **Create database**.

**2. Choose PostgreSQL with pgvector**

Click **PostgreSQL**, choose the latest version, and turn on **Enable pgvector extension**.

**3. Name and place it**

Enter the **Display name** `openwebui-db`, choose the **Data center** where LiteLLM and Ollama run, and choose the **Size**. This guide was tested on **DB1**.

**4. Create it**

Click **Create database**. Wait for the status **Ready**.

Use a database of its own for Open WebUI, not the one LiteLLM uses: each application then has its own backups, size and upgrade schedule.

## Deploy Open WebUI

**1. Create the application**

Click **Applications**, then click **Create** > **Application**. Under **Source**, click **Docker image** and enter `ghcr.io/open-webui/open-webui:v0.12.5-slim` in **Docker image**. Keep the **Registry credential** at **None (public image)**. Enter the **Name** `open-webui`, choose the same **Data center**, choose the **Size** **S1**, then click **Create application**. Don't deploy yet.

**2. Add a disk for uploads**

Click the **Disks** tab, then click **Create disk**. In **Process**, pick the web process. Enter the **Path** `/app/backend/data`, pick the **Size** `10 GB`, and click **Create disk**.

**3. Connect the database**

Click the **Networking** tab. Under **Connected services**, click **Add internal connection** and choose `openwebui-db` as the **Service**. Turn on **Add environment variables to the application**, change the key `DB_URL` to `DATABASE_URL`, and click **Add internal connection**.

**4. Connect LiteLLM and Ollama**

Click **Add internal connection** again and choose `litellm`, then once more for `ollama`. They need no variables.

**5. Add the environment variables**

Click the **Environment variables** tab, then click **Add environment variables**. Add the variables from the table after these steps, then click **Add environment variables**.

**6. Add a readiness probe**

Click the **Processes** tab, click **⋯** on the web process, then click **Update process**. Under **Health checks**, turn on **Readiness probe**, enter the **Path** `/health` and the **Initial delay** `10`, then click **Update process**.

**7. Deploy**

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

| Variable | Value | Why |
| - | - | - |
| `WEBUI_SECRET_KEY` | A random value, for example from `openssl rand -hex 32` | Signs sign-in sessions. Without it, the image generates a key into a file outside the disk, so each deployment signs everyone out |
| `WEBUI_ADMIN_EMAIL` | Your email address | With `WEBUI_ADMIN_PASSWORD`, creates the admin account on the first start and turns sign-up off. Without them, the first person to sign up on the domain becomes the admin |
| `WEBUI_ADMIN_PASSWORD` | A strong password | The admin's password. It's used only while there are no users |
| `OPENAI_API_BASE_URL` | `http://` plus LiteLLM's internal hostname plus `:8080/v1`, for example `http://litellm-q9w3e-web.litellm-q9w3e.svc.cluster.local:8080/v1` | The chat connection. Every model in LiteLLM's `config.yaml` appears in the model picker |
| `OPENAI_API_KEY` | The virtual key you created for Open WebUI in LiteLLM | LiteLLM tracks the interface's spend and budget under this key |
| `OLLAMA_BASE_URL` | `http://` plus Ollama's internal hostname plus `:8080`, for example `http://ollama-k3j4h-web.ollama-k3j4h.svc.cluster.local:8080` | The Ollama connection, used here for document embeddings |
| `ENABLE_OLLAMA_API` | `False` | Keeps Ollama's own models out of the model picker, so every chat goes through LiteLLM's key and spend tracking. Embeddings still use `OLLAMA_BASE_URL` |
| `RAG_EMBEDDING_ENGINE` | `ollama` | The slim image has no local embedding model, so documents are embedded by Ollama |
| `RAG_EMBEDDING_MODEL` | `embeddinggemma-2:270m` | The Ollama model that turns document text into vectors for pgvector |

Copy each internal hostname from **Internal hostnames** on the `litellm` and `ollama` applications' **Networking** tabs.

## Verify the deployment

1. Wait for the deployment's status to change to **Deployed**. The first start creates the tables in PostgreSQL and the admin account.

2. Check the health endpoint on the application's domain:

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

   ```json theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
   {"status":true}
   ```

3. Open the domain and sign in with `WEBUI_ADMIN_EMAIL` and `WEBUI_ADMIN_PASSWORD`. The model picker lists LiteLLM's models, for example `lfm3`, `claude-haiku-5-5` and `gpt-6-luna`. To check the same without a browser, sign in through the API and list the models:

   ```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
   TOKEN=$(curl -s https://open-webui-x7k2m.sevalla.app/api/v1/auths/signin \
     -H "Content-Type: application/json" \
     -d '{"email": "admin@example.com", "password": "<WEBUI_ADMIN_PASSWORD>"}' | jq -r .token)
   curl -s https://open-webui-x7k2m.sevalla.app/api/models -H "Authorization: Bearer $TOKEN" | jq -r '.data[].id'
   ```

   ```text theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
   lfm3
   embeddinggemma
   claude-haiku-5-5
   gpt-6-luna
   ```

4. Choose a model and send a message. The answer appears word by word as the model writes it.

After the first sign-in, you can delete `WEBUI_ADMIN_PASSWORD` on the **Environment variables** tab: Open WebUI stores a hash of the password in the database.

## Streaming and WebSockets

The browser receives chat answers over a WebSocket at `wss://<domain>/ws/socket.io/`, which Cloudflare and Sevalla's proxy pass through without settings. In a test in Chromium, the 513-character answer from `lfm3` arrived in 49 updates over 5 seconds; `claude-haiku-5-5` and `gpt-6-luna` started answering after 1.2 s and 4.5 s.

Open WebUI's API streams too. Called with a bearer token, such as the token that `/api/v1/auths/signin` returns, `/api/chat/completions` with `"stream": true` returns `Content-Type: text/event-stream` with `Cache-Control: no-cache`, and the events reach the client unbuffered: the first of 42 arrived after 1.40 s and the last after 5.48 s.

## Speed and cost of each chat

Open WebUI adds instructions and the definitions of its built-in tools (calendar, notes, memory and others) to each chat. A one-line question reached the models as 2,050 prompt tokens for `lfm3` on Ollama, 4,410 for `gpt-6-luna` and 9,763 for `claude-haiku-5-5`:

* **CPU models** read the whole prompt before they write the first word: 41 s for those 2,050 tokens on an Ollama on **S2**. Small models also misuse the tools: asked to write 50 words, `lfm3` called `create_calendar_event` instead. Turn the built-in tools off for local models: in **Admin Panel** > **Settings** > **Models**, open the model and turn off **Builtin Tools** under **Capabilities**. The same question was then a 25-token prompt, and the answer streamed without a tool call.
* **Hosted models** bill these tokens. One question each, plus the titles, tags and follow-up suggestions Open WebUI generated afterward, cost \$0.002 in total for both providers, in LiteLLM's spend for the key.

Ollama answers one request at a time by default, so a chat also waits while Ollama finishes the title and follow-up suggestions of the chat before it.

## Upload documents

Attach a file to a chat, or add it to a knowledge base. Open WebUI saves it under `/app/backend/data/uploads` on the disk, has Ollama embed its text with `embeddinggemma-2:270m`, and stores the vectors in PostgreSQL. When the file shows as processed, ask about its content in the chat.

## Let other people sign in

The admin account turns sign-up off. To let your team in:

1. In Open WebUI, open **Admin Panel** > **Settings** > **Authentication** and turn on **New Sign Ups**.
2. New accounts get the **Default User Role**, **pending** unless you change it: an admin approves each one under **Admin Panel** > **Users**.

Keep sign-up off when nobody else should use the interface: every user's chats spend from Open WebUI's LiteLLM key.

## Change settings after the first start

The first start copies the connection, sign-up and document settings from the environment variables into the database. After that, the database wins: changing `OPENAI_API_KEY` on the **Environment variables** tab and deploying again left Open WebUI on the old key. Change these settings in Open WebUI instead, for example the LiteLLM key under **Admin Panel** > **Settings** > **Connections**, in **OpenAI API**.

To make the variables win on every start, set `ENABLE_PERSISTENT_CONFIG` to `False`. Settings you change in the **Admin Panel** then don't survive a restart, including the switch that turned sign-up off when the admin account was created, so also set `ENABLE_SIGNUP` to `False`.

To add models from many providers as one more connection, see [OpenRouter in Open WebUI](https://docs.sevalla.com/guides/ai/openrouter.md#open-webui).

## Run more than one instance

This guide runs 1 instance: a process with a disk can't run more. A larger size gives the instance more room. More instances need uploads in object storage instead of a disk and Redis for the WebSocket connections (`WEBSOCKET_MANAGER=redis`), which this guide doesn't cover.

## Update Open WebUI

**1. Back up the database**

Create a backup of `openwebui-db` on its **Backups** tab: a new version can change the database schema when it starts. See [Backups](https://docs.sevalla.com/databases/backups.md). Sevalla [backs up the disk](https://docs.sevalla.com/applications/disks.md#backups) every day.

**2. Deploy the new version**

On the application's **Deployments** tab, click **Deploy now**. Enter the new **Tag**, with `-slim` at the end, for example the latest from [Open WebUI's releases](https://github.com/open-webui/open-webui/releases), and click **Deploy**.

**3. Keep the new version**

When it works, on the **Settings** tab, in the **Source** section, click **Update source**, enter the new image in **Docker image**, and click **Update source**.

The disk is moved to the new instance, so Open WebUI is unavailable for a few moments during each deployment.

## Add a custom domain

To serve Open WebUI from your own domain, such as `chat.example.com`, add the domain on the application's **Domains** tab and create the DNS records that Sevalla shows. **S1** is larger than **H1**, so custom domains work. See [Custom domains](https://docs.sevalla.com/domains/overview.md).

## Troubleshoot

| Symptom | Cause | Fix |
| - | - | - |
| The model picker shows no LiteLLM models | The key is wrong or deleted, or LiteLLM has no internal connection from Open WebUI | Check the key in **Admin Panel** > **Settings** > **Connections**, and the connection on the **Networking** tab |
| A changed environment variable has no effect | The setting was copied into the database on the first start | Change it in the **Admin Panel**. See [Change settings after the first start](#change-settings-after-the-first-start) |
| Uploads fail, and the runtime logs show `401, message='Unauthorized', url='https://api.openai.com/v1/embeddings'` | `RAG_EMBEDDING_ENGINE` is `openai` without `RAG_OPENAI_API_BASE_URL`, so Open WebUI sends embeddings to OpenAI | Use the `ollama` engine as in this guide, or set the engine in **Admin Panel** > **Settings** > **Documents** |
| Everyone is signed out after each deployment | `WEBUI_SECRET_KEY` isn't set | Add it, then deploy |
| The first word takes tens of seconds with an Ollama model | The CPU reads Open WebUI's long prompt first | See [Speed and cost of each chat](#speed-and-cost-of-each-chat) |

## Deploy with the API

The same deployment as the dashboard steps, as API calls an agent can run in order. It needs:

* An [API key](https://docs.sevalla.com/developer-tools/api-keys.md) that can create applications and databases, in `SEVALLA_API_KEY`.
* The IDs of the [LiteLLM](https://docs.sevalla.com/guides/ai/litellm.md#deploy-with-the-api) and [Ollama](https://docs.sevalla.com/guides/ai/ollama.md#deploy-with-the-api) applications, and Open WebUI's virtual key from LiteLLM.
* `curl`, `jq` and `openssl`.

The image is public, so no Git provider or registry credential is involved. Each step names the API operation, the CLI command (`@sevalla/cli` 2.1.0) and the tool of the [Sevalla MCP server](https://docs.sevalla.com/ai-agents/overview.md).

**1. Set the variables**

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
export SEVALLA_API=https://api.sevalla.com/v3
export SEVALLA_API_KEY=<your API key>
LITELLM_APP_ID=<the LiteLLM application's ID>
OLLAMA_APP_ID=<the Ollama application's ID>
OPENWEBUI_KEY=<Open WebUI's virtual key from LiteLLM>
ADMIN_EMAIL=admin@example.com
ADMIN_PASSWORD=$(openssl rand -base64 18)
```

Keep `ADMIN_PASSWORD`: it's the admin account's password.

**2. Find the location**

[List clusters](https://api-docs.sevalla.com/v3/resources/list-clusters) (`listClustersV3`):

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
CLUSTER_ID=$(curl -s "$SEVALLA_API/resources/clusters" \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  | jq -r '.[] | select(.location == "eu-frankfurt-1") | .id')
```

Replace `eu-frankfurt-1` with the region ID of your location from [Locations](https://docs.sevalla.com/platform/locations.md#locations-on-oracle-cloud-infrastructure), and pick by region ID, not by display name. CLI: `sevalla resources clusters`. MCP: `get_platform_options`.

**3. Look up the sizes and the PostgreSQL version**

[List database resource types](https://api-docs.sevalla.com/v3/resources/list-database-resource-types) (`listDatabaseResourceTypesV3`), [list disk resource types](https://api-docs.sevalla.com/v3/resources/list-disk-resource-types) (`listDiskResourceTypesV3`) and [list database versions](https://api-docs.sevalla.com/v3/resources/list-database-versions) (`listDatabaseVersionsV3`):

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
DB_SIZE_ID=$(curl -s "$SEVALLA_API/resources/database-resource-types" \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  | jq -r '.[] | select(.name == "db1") | .id')
DISK_SIZE_ID=$(curl -s "$SEVALLA_API/resources/disk-resource-types" \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  | jq -r '.[] | select(.size_gib == 10) | .id')
PG_VERSION=$(curl -s "$SEVALLA_API/resources/database-versions?cluster_id=$CLUSTER_ID&type=postgresql" \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  | jq -r '.[0].versions | last')
```

CLI: `sevalla resources db-types`, `sevalla resources disk-types` and `sevalla resources db-versions --cluster $CLUSTER_ID --type postgresql`. MCP: `get_platform_options`, and `sevalla_api_read` with `listDiskResourceTypesV3`.

**4. Create the database with pgvector**

[Create database](https://api-docs.sevalla.com/v3/databases/create-database) (`createDatabaseV3`) with `extensions.enable_vector`, then [get database](https://api-docs.sevalla.com/v3/databases/get-database) (`getDatabaseV3`) until its `status` is `ready`. Keep `DB_PASSWORD`: the API doesn't return it:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
DB_PASSWORD=$(openssl rand -hex 16)
DB_ID=$(curl -s -X POST "$SEVALLA_API/databases" \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"display_name\": \"openwebui-db\", \"type\": \"postgresql\", \"version\": \"$PG_VERSION\", \"cluster_id\": \"$CLUSTER_ID\", \"resource_type_id\": \"$DB_SIZE_ID\", \"db_name\": \"openwebui\", \"db_password\": \"$DB_PASSWORD\", \"extensions\": {\"enable_vector\": true}}" \
  | jq -r .id)
for i in $(seq 1 120); do
  DB_STATUS=$(curl -s "$SEVALLA_API/databases/$DB_ID" \
    -H "Authorization: Bearer $SEVALLA_API_KEY" | jq -r '.status // empty')
  [ "$DB_STATUS" = "ready" ] && break
  sleep 15
done
echo "$DB_STATUS"
```

CLI: not available, because `sevalla databases create` has no option for extensions. MCP: `create_database` with `"extensions": {"vector": true}`; it generates the password itself and never returns it, and with `connect_to_application_id` and `env_var_name` `DATABASE_URL` it stores a `postgresql://` URL, which Open WebUI accepts.

**5. Create the application**

[Create application](https://api-docs.sevalla.com/v3/applications/create-application) (`createApplicationV3`). It doesn't deploy, and it creates the web process at **S1**, the size this guide uses:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
APP_ID=$(curl -s -X POST "$SEVALLA_API/applications" \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"display_name\": \"open-webui\", \"cluster_id\": \"$CLUSTER_ID\", \"source\": \"dockerImage\", \"docker_image\": \"ghcr.io/open-webui/open-webui:v0.12.5-slim\"}" \
  | jq -r .id)
```

CLI: `sevalla apps create --name open-webui --cluster $CLUSTER_ID --source dockerImage --docker-image ghcr.io/open-webui/open-webui:v0.12.5-slim`. MCP: `create_application` with `"deploy": false`.

**6. Add the readiness probe**

[List processes](https://api-docs.sevalla.com/v3/applications/list-processes) (`listProcessesV3`), then [update process](https://api-docs.sevalla.com/v3/applications/update-process) (`updateProcessV3`). Send `timeoutSeconds`: a probe set through the API without it waits 120 seconds, not the dashboard's 1:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
WEB_ID=$(curl -s "$SEVALLA_API/applications/$APP_ID/processes" \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  | jq -r '.data[] | select(.type == "web") | .id')
curl -s -X PATCH "$SEVALLA_API/applications/$APP_ID/processes/$WEB_ID" \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"readiness_probe": {"httpGet": {"path": "/health", "port": 8080}, "initialDelaySeconds": 10, "periodSeconds": 10, "timeoutSeconds": 1, "successThreshold": 1, "failureThreshold": 3}}' \
  | jq -c '{resource_type_name, readiness_probe}'
```

CLI: not available; `sevalla apps processes update` has no probe options. MCP: `sevalla_api_update` with `updateProcessV3` and the same body.

**7. Add the disk**

[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 PUT "$SEVALLA_API/applications/$APP_ID/processes/$WEB_ID/disk" \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"resource_type_id\": \"$DISK_SIZE_ID\", \"mount_path\": \"/app/backend/data\"}"
```

CLI: `sevalla apps processes disk update $WEB_ID --app-id $APP_ID --resource-type $DISK_SIZE_ID --mount-path /app/backend/data`. MCP: `sevalla_api_update` with `updateProcessDiskV3`.

**8. Connect the database, LiteLLM and Ollama**

[Create internal connection](https://api-docs.sevalla.com/v3/applications/create-internal-connection) (`createApplicationInternalConnectionV3`), once for each:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
connect() {
  curl -s -X POST "$SEVALLA_API/applications/$APP_ID/internal-connections" \
    -H "Authorization: Bearer $SEVALLA_API_KEY" \
    -H "Content-Type: application/json" \
    -d "{\"target_id\": \"$1\", \"target_type\": \"$2\"}"
}
connect "$DB_ID" database
connect "$LITELLM_APP_ID" app
connect "$OLLAMA_APP_ID" app
```

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

**9. Add the environment variables**

The API connections add no variables, so build `DATABASE_URL` in the dashboard's format, `postgres://<user>:<password>@<host>:<port>/<database>`, from [get database](https://api-docs.sevalla.com/v3/databases/get-database), and the two URLs from the web processes' internal hostnames. Then [create environment variable](https://api-docs.sevalla.com/v3/applications/create-environment-variable) (`createEnvironmentVariableV3`) for each:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
DATABASE_URL=$(curl -s "$SEVALLA_API/databases/$DB_ID" \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  | jq -r --arg pw "$DB_PASSWORD" '"postgres://\(.db_user):\($pw)@\(.internal_hostname):\(.internal_port)/\(.db_name)"')
hostname_of() {
  curl -s "$SEVALLA_API/applications/$1/processes" \
    -H "Authorization: Bearer $SEVALLA_API_KEY" \
    | jq -r '.data[] | select(.type == "web") | .internal_hostname'
}
add_var() {
  jq -n --arg key "$1" --arg value "$2" '{key: $key, value: $value, is_runtime: true, is_buildtime: false}' \
    | curl -s -X POST "$SEVALLA_API/applications/$APP_ID/env-vars" \
      -H "Authorization: Bearer $SEVALLA_API_KEY" \
      -H "Content-Type: application/json" \
      -d @- \
    | jq -r .key
}
add_var DATABASE_URL "$DATABASE_URL"
add_var WEBUI_SECRET_KEY "$(openssl rand -hex 32)"
add_var WEBUI_ADMIN_EMAIL "$ADMIN_EMAIL"
add_var WEBUI_ADMIN_PASSWORD "$ADMIN_PASSWORD"
add_var OPENAI_API_BASE_URL "http://$(hostname_of "$LITELLM_APP_ID"):8080/v1"
add_var OPENAI_API_KEY "$OPENWEBUI_KEY"
add_var OLLAMA_BASE_URL "http://$(hostname_of "$OLLAMA_APP_ID"):8080"
add_var ENABLE_OLLAMA_API False
add_var RAG_EMBEDDING_ENGINE ollama
add_var RAG_EMBEDDING_MODEL embeddinggemma-2:270m
```

CLI: `sevalla apps env-vars create $APP_ID --key ENABLE_OLLAMA_API --value False`, once per variable. MCP: `set_environment_variables`.

**10. Deploy**

[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 `success` or `failed`, for at most 30 minutes:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
DEPLOYMENT_ID=$(curl -s -X POST "$SEVALLA_API/applications/$APP_ID/deployments" \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}' \
  | jq -r .id)
for i in $(seq 1 120); do
  STATUS=$(curl -s "$SEVALLA_API/applications/$APP_ID/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"
```

If it fails, read [Get application deployment logs](https://api-docs.sevalla.com/v3/applications/get-application-deployment-logs) (`getApplicationDeploymentLogsV3`) and the runtime logs. CLI: `sevalla apps deployments trigger $APP_ID`, then `sevalla apps deployments get $DEPLOYMENT_ID --app-id $APP_ID`. MCP: `trigger_deployment`, then `get_deployment`.

**11. Check the deployment**

[List application domains](https://api-docs.sevalla.com/v3/applications/list-application-domains) (`listApplicationDomainsV3`) for the domain, then call Open WebUI:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
DOMAIN=$(curl -s "$SEVALLA_API/applications/$APP_ID/domains" \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  | jq -r '.data[] | select(.is_primary) | .name')
curl -s "https://$DOMAIN/health"; echo
TOKEN=$(jq -n --arg email "$ADMIN_EMAIL" --arg password "$ADMIN_PASSWORD" '{email: $email, password: $password}' \
  | curl -s "https://$DOMAIN/api/v1/auths/signin" -H "Content-Type: application/json" -d @- \
  | jq -r .token)
curl -s "https://$DOMAIN/api/models" -H "Authorization: Bearer $TOKEN" | jq -r '.data[].id'
```

```text theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
{"status":true}
lfm3
embeddinggemma
claude-haiku-5-5
gpt-6-luna
```

CLI: `sevalla apps domains list --app-id $APP_ID`. MCP: `get_application`, which lists the domains.


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