> ## 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 a Gradio application

> Deploy a Gradio 6 app to Sevalla with uv, built by Ingot or your own Dockerfile: launch() on the PORT variable, the event queue through Sevalla's proxy, and health checks.

A Gradio app runs on Sevalla as an application: [Ingot](https://docs.sevalla.com/applications/builds/ingot.md), Sevalla's builder, installs its dependencies from `uv.lock`, finds `app.py` and runs it with `python app.py`, after setting `GRADIO_SERVER_NAME` to `0.0.0.0` and `GRADIO_SERVER_PORT` to the port in the `PORT` environment variable. `demo.launch()` reads both, so the app listens where Sevalla sends traffic. The browser calls the app's functions through Gradio's queue, over server-sent events that Sevalla's proxy passes through.

## At a glance

| | |
| - | - |
| Source | A Git repository |
| Build | [Ingot](https://docs.sevalla.com/applications/builds/ingot.md) (Python 3.14, uv), or the [Dockerfile](#deploy-with-a-dockerfile) |
| Start command | `export GRADIO_SERVER_NAME=0.0.0.0 GRADIO_SERVER_PORT="$PORT" && exec python app.py`, which Ingot detects from `app.py` |
| Port | The `PORT` environment variable, `8080` by default |
| Processes | Web process, 1 instance |
| Services | None |
| Disk | None |
| Health check | `GET /` returns `200` (readiness probe) |
| Size | **H1** |

### Environment variables

| Variable | Required | Available during | Secret | Value |
| - | - | - | - | - |
| `PORT` | No | Runtime | No | Set by Sevalla to the web process port |
| `GRADIO_ANALYTICS_ENABLED` | No | Runtime | No | `False` turns off the usage analytics Gradio sends |

## Before you begin

* A Sevalla company with a payment method, and a GitHub, GitLab or Bitbucket account for the repository. See [Git providers](https://docs.sevalla.com/git/overview.md).
* Python 3.14 and [uv](https://docs.astral.sh/uv/) 0.13 or later on your computer.

## Create the project

Create the project with uv and add Gradio:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
uv init --app --no-package --python 3.14 quote-tool
cd quote-tool
uv add gradio
rm main.py
```

`uv init` writes `pyproject.toml`, a `.python-version` with `3.14`, a `.gitignore` that keeps out `.venv`, and a Git repository. `--no-package` keeps the project a script rather than a package to install. `uv add` installs Gradio 6.31 into `.venv` and pins every package in `uv.lock`. The app replaces the `main.py` that `uv init` writes:

```python app.py theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
import gradio as gr


def quote(quantity: float, unit_price: float) -> str:
    return f"Total: ${quantity * unit_price:,.2f}"


demo = gr.Interface(
    fn=quote,
    inputs=[
        gr.Number(label="Quantity", value=1, precision=0),
        gr.Number(label="Unit price", value=9.99),
    ],
    outputs=gr.Textbox(label="Quote"),
    title="Storefront quote",
    flagging_mode="never",
)

if __name__ == "__main__":
    demo.launch()
```

`flagging_mode="never"` removes the **Flag** button. Flagged examples go to `.gradio/flagged` in the app's folder, and Sevalla resets an application's files on every deployment, so they wouldn't last.

Run it with `uv run app.py` and open `http://127.0.0.1:7860`. Set **Quantity** to `3` and click **Submit**: **Quote** shows `Total: $29.97`. Press `Ctrl+C` to stop it.

## Prepare the project for Sevalla

The project already has what Ingot needs:

| File | Why Sevalla needs it |
| - | - |
| `.python-version` | Ingot builds on the Python line it names, 3.14. Without it, Ingot uses its default line, which a new Ingot release can move. See [Pin the Python version](https://docs.sevalla.com/guides/python/overview.md#pin-the-python-version). |
| `uv.lock` | Ingot installs the exact versions in it with `uv sync --locked`, into a virtual environment at `/app/.venv` that's on the `PATH`. Commit it. |
| `app.py` | The script Ingot runs when Gradio is a dependency: the first of `main.py`, `app.py` and `server.py` that exists. |

Ingot starts the web process with:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
export GRADIO_SERVER_NAME=0.0.0.0 GRADIO_SERVER_PORT="$PORT" && exec python app.py
```

* Gradio listens on all interfaces, on the port that Sevalla sets in `PORT` (`8080` by default). Call `demo.launch()` without `server_name` or `server_port`: arguments to `launch()` win over the variables.
* Don't set `share=True`. It creates a temporary public link through Gradio's own servers; on Sevalla, the application's domain is the link. The runtime logs still print `To create a public link, set share=True in launch()`, which you can ignore.

To start the app another way, set the web process's start command; see [Set the start command](https://docs.sevalla.com/applications/builds/ingot.md#set-the-start-command).

Commit everything and push it to your Git provider. `uv init` already created the repository:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
git add .
git commit -m "Prepare for Sevalla"
git branch -M main
git remote add origin https://github.com/acme/quote-tool.git
git push -u origin main
```

## Deploy to Sevalla

These steps build with [Ingot](https://docs.sevalla.com/applications/builds/ingot.md). To build from your own Dockerfile instead, see [Deploy with a Dockerfile](#deploy-with-a-dockerfile).

**1. Create the application**

In the sidebar, click **Applications**, then click **Create** > **Application**. Under **Source**, click **Private repository**, then choose the **Git provider**, the **Repository** and the **Branch**. Under **Location**, choose a **Data center**. Keep the **Size** at **H1** or choose a larger one, then click **Create application**. The application opens on its **Overview** tab with the status **Not deployed yet**: creating an application doesn't deploy it.

**2. Deploy**

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

**3. Turn on auto-deploy**

To deploy every push to the branch automatically, click the **Settings** tab, then click **Update auto-deploy** in the **Auto-deploy** section. Turn on **Auto-deploy** and click **Update auto-deploy**. See [Deployments](https://docs.sevalla.com/applications/deployments.md#deploy-automatically-on-push).

## Verify the deployment

1. On the **Deployments** tab, click the deployment. Its **Build log** shows Ingot's [build plan](https://docs.sevalla.com/applications/builds/ingot.md#read-the-build-plan): Python 3.14 from `.python-version`, uv from `uv.lock`, and Gradio with the start command for `app.py`.

   ```text Build log theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
   ╭─ ■ Gradio · Python 3.14 ────────────────────────────────────────────────────────────────────────────────────────────────────╮
   │ Python           3.14                                                                                .python-version "3.14" │
   │ Package manager  uv                                                                                  uv.lock                │
   │ uv               0.13                                                                                default                │
   │ Framework        Gradio                                                                              app.py                 │
   │ Start            export GRADIO_SERVER_NAME=0.0.0.0 GRADIO_SERVER_PORT="$PORT" && exec python app.py  Gradio                 │
   ├─ Image ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
   │ Build            python:3.14-slim-trixie                                                                                    │
   │ Runtime          python:3.14-slim-trixie                                                                                    │
   │ User             1000:1000                                                                                                  │
   │ Init             tini                                                                                                       │
   │ Port             $PORT, default 8080                                                                                        │
   ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────── Ingot v0.4.1 ─╯
   ```

2. Wait for the deployment's status to change to **Deployed**. The application's status next to its name changes to **Running**, and the web process's [runtime logs](https://docs.sevalla.com/applications/logs.md) show `* Running on local URL:  http://0.0.0.0:8080`.

3. Click the domain under the application's name, for example `quote-tool-x7k2m.sevalla.app`. Set **Quantity** to `3` and click **Submit**: **Quote** shows `Total: $29.97`.

4. Call the same function from your computer with `gradio_client`, which comes with Gradio. Use your application's domain:

   ```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
   uv run python -c "from gradio_client import Client; print(Client('https://quote-tool-x7k2m.sevalla.app/').predict(3, 9.99, api_name='/quote'))"
   ```

   It prints `Loaded as API: https://quote-tool-x7k2m.sevalla.app/`, then `Total: $29.97`. Gradio names each function's endpoint after the function, so `quote` is `/quote`.

5. To check the deployment through the API, use `APP_ID` and `DEPLOYMENT_ID` from [Deploy with the API](#deploy-with-the-api). [Get deployment](https://api-docs.sevalla.com/v3/applications/get-deployment) (`getDeploymentV3`) returns `success`, and [Get application runtime logs](https://api-docs.sevalla.com/v3/applications/get-application-runtime-logs) (`getApplicationRuntimeLogsV3`) finds Gradio's start line:

   ```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
   curl -s https://api.sevalla.com/v3/applications/$APP_ID/deployments/$DEPLOYMENT_ID \
     -H "Authorization: Bearer $SEVALLA_API_KEY" | jq -r .status
   curl -s "https://api.sevalla.com/v3/applications/$APP_ID/runtime-logs?search=Running%20on" \
     -H "Authorization: Bearer $SEVALLA_API_KEY" | jq -r '.[].message'
   ```

   The first command prints `success`, and the second `* Running on local URL:  http://0.0.0.0:8080`. The CLI has `sevalla apps deployments get` and `sevalla apps logs runtime`, and the MCP server `get_deployment` and `search_runtime_logs`.

![The application header with its name, Running status, repository, branch and the domain that opens the application](https://assets.sevalla.com/screenshots/light/app-overview-address.png)

If the deployment fails, read its build log, then see [Troubleshoot Ingot builds](https://docs.sevalla.com/applications/builds/ingot.md#troubleshoot-ingot-builds). If it deploys but the page doesn't load, read the application's [runtime logs](https://docs.sevalla.com/applications/logs.md).

## Keep the web process at one instance

Gradio keeps its queue in the memory of the instance that runs it: a call joins the queue with one request, then reads its result from a second, long-lived request. Sevalla doesn't keep a visitor's requests on one instance, so with 2 or more instances a call whose second request reaches another instance fails, and its event stream reports `404: Not Found`. Keep the web process at 1 instance, and choose a larger size when the app needs more CPU or memory. See [Scaling](https://docs.sevalla.com/applications/scaling.md).

## Add a health check

Gradio takes a few seconds to import and start. Without a readiness probe, a new instance receives traffic as soon as its container starts, and requests in those seconds fail with `503`. Point the probe at `/`, which answers once Gradio listens:

**1. Open the web process**

On the application's **Processes** tab, click **⋯** on the **Web** process, then click **Update process**.

**2. Turn on the readiness probe**

Under **Health checks**, turn on **Readiness probe** and set the **Path** to `/`. Keep the **Port** at the web process's port, `8080` unless you changed it.

**3. Save and deploy**

Click **Update process**, then click **Deploy** in the undeployed changes bar at the bottom of the page.

The deployment reaches **Deployed** once the new instance passes the probe. See [Health checks](https://docs.sevalla.com/applications/processes.md#health-checks).

## Deploy with a Dockerfile

Ingot needs no Dockerfile. Write your own when you want full control of the image: the base image, the system packages and every layer. Add a `Dockerfile` and a `.dockerignore` to the root of the repository:

```dockerfile Dockerfile theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
FROM python:3.14-slim-trixie AS build
COPY --from=ghcr.io/astral-sh/uv:0.13 /uv /usr/local/bin/
ENV UV_COMPILE_BYTECODE=1 \
    UV_PYTHON_DOWNLOADS=0
WORKDIR /app
COPY pyproject.toml uv.lock .python-version ./
RUN uv sync --locked --no-dev

FROM python:3.14-slim-trixie
# hadolint ignore=DL3008
RUN apt-get update \
    && apt-get install -y --no-install-recommends tini \
    && rm -rf /var/lib/apt/lists/*
RUN useradd --uid 1000 --create-home app
ENV PATH="/app/.venv/bin:$PATH" \
    PYTHONUNBUFFERED=1 \
    PORT=8080 \
    GRADIO_SERVER_NAME=0.0.0.0
WORKDIR /app
COPY --from=build /app/.venv .venv
COPY . .
USER 1000:1000
EXPOSE 8080
ENTRYPOINT ["tini", "--"]
CMD ["sh", "-c", "GRADIO_SERVER_PORT=\"$PORT\" exec python app.py"]
```

| Line or choice | Why |
| - | - |
| `python:3.14-slim-trixie` | The Python line that `.python-version` pins, on Debian 13, as Ingot builds it. A line tag picks up each patch release and security fix when you deploy again; a digest would stay on one release. |
| Two stages | The first stage installs the packages with uv. The final image gets only the virtual environment and the code, not uv. |
| `ghcr.io/astral-sh/uv:0.13` | uv's official image, which Ingot uses too. `COPY --from` takes its `uv` binary without a download script. |
| `uv sync --locked --no-dev` | Installs exactly what `uv.lock` pins, and fails if the lock file doesn't match `pyproject.toml`. `UV_COMPILE_BYTECODE=1` compiles the packages at build time, so instances start faster. `UV_PYTHON_DOWNLOADS=0` makes uv use the image's Python rather than download one. |
| Lock files before the code | Docker reuses the installed packages until `pyproject.toml`, `uv.lock` or `.python-version` change, so a code change rebuilds only the last layers. |
| `tini` | Gradio doesn't handle `SIGTERM`, and a process running as PID 1 ignores signals it doesn't handle, so without an init process each deployment waits until the instance is killed. `tini` runs as PID 1 and passes `SIGTERM` on, so Gradio stops at once. It comes from Debian's packages. The `hadolint ignore` comment skips the linter's rule that asks for a pinned package version: the `trixie` tag already holds `tini` to Debian 13's version, and a pinned patch version would break the build when Debian ships a fix. |
| `useradd` and `USER 1000:1000` | Runs Gradio as an unprivileged user with a home directory, where Gradio's Hugging Face client keeps its cache. The code and packages stay owned by root, so the app can't change them; Gradio writes its temporary files to `/tmp`. |
| `GRADIO_SERVER_NAME` and `GRADIO_SERVER_PORT` | The same variables Ingot sets. A shell reads `PORT` at start, and `exec` replaces it with Python. |

```text .dockerignore theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
.git
.venv
**/__pycache__
.env*
Dockerfile
.dockerignore
```

Commit and push both files. Ingot doesn't use the `Dockerfile`, so you can keep it in a repository that builds with Ingot, but it adds the `.dockerignore` patterns to its own: its build plan shows an **Ignored** row with `6 patterns from .dockerignore`. Then switch the application to the Dockerfile build strategy:

**1. Choose the Dockerfile build strategy**

On the application's **Settings** tab, 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**.

**2. Deploy**

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

![The Update build strategy sheet with Dockerfile selected, the Dockerfile path, the Docker context and the Docker registry credential](https://assets.sevalla.com/screenshots/light/app-settings-build-strategy-dockerfile.png)

The build log shows `📦 Building and pushing docker image using Dockerfile...` where Ingot's build plan was. The app and `gradio_client` return `Total: $29.97` as before, and the deployment's **Dockerfile** tab shows your Dockerfile. See [Dockerfile](https://docs.sevalla.com/applications/builds/dockerfile.md).

## Deploy changes

With **Auto-deploy** on, every push to the branch starts a deployment; otherwise, click **Deploy now** for each change. Each deployment builds the image, then replaces the web process's instance. After you add a package with `uv add`, commit `pyproject.toml` and `uv.lock` together. See [Deployments](https://docs.sevalla.com/applications/deployments.md).

## Use environment variables

Keep API keys and other secrets, such as a model provider's key, in [environment variables](https://docs.sevalla.com/applications/environment-variables.md) and read them with `os.environ`, for example `os.environ["SALES_API_TOKEN"]`. Add them on the application's **Environment variables** tab, keep **Available during runtime** selected, and deploy: variables apply with the next deployment.

Gradio reads its own settings from variables that start with `GRADIO_`. For example, `GRADIO_ANALYTICS_ENABLED` set to `False` turns off the usage analytics Gradio sends by default.

## Add a custom domain

To serve the app from your own domain, such as `quote.example.com`, add the domain on the application's **Domains** tab and create the DNS records that Sevalla shows. No code change is needed. Custom domains need a web process larger than **H1**, so [scale](https://docs.sevalla.com/applications/scaling.md) the web process first. See [Custom domains](https://docs.sevalla.com/domains/overview.md).

## Deploy with the API

These calls make the same deployment as [Deploy to Sevalla](#deploy-to-sevalla), in an order that an agent or a script runs as is. Each step names the API operation, then the [CLI](https://docs.sevalla.com/developer-tools/cli.md) command and the [MCP](https://docs.sevalla.com/ai-agents/overview.md) tool that do the same. The CLI reads the key from `SEVALLA_API_TOKEN` and prints tables; with `--json` it prints the API's JSON, except that lists are bare arrays rather than the API's `data` field.

Before you start:

* Put an API key in `SEVALLA_API_KEY`. The key needs the **Company developer** role or **Company admin**; see [API keys](https://docs.sevalla.com/developer-tools/api-keys.md). The commands use `curl` and `jq`.
* Push the repository and connect its Git account to Sevalla, as in [Before you begin](#before-you-begin). No API call reports whether the account is connected: if it isn't, or Sevalla's GitHub App can't see the repository, creating the application fails with `400` and a message that starts with `Cannot access repository https://github.com/acme/quote-tool: Repository not found or you don't have access.` and says where to grant access.
* Look up the location and the size. The first command picks the location by its region ID, here `eu-frankfurt-1` (Frankfurt, Germany); see [Locations](https://docs.sevalla.com/platform/locations.md) for the others. [List clusters](https://api-docs.sevalla.com/v3/resources/list-clusters) (`listClustersV3`) and [List process resource types](https://api-docs.sevalla.com/v3/resources/list-process-resource-types) (`listProcessResourceTypesV3`) return them; the CLI has `sevalla resources clusters` and `sevalla resources process-types`, and the MCP server `get_platform_options`.

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
CLUSTER_ID=$(curl -s https://api.sevalla.com/v3/resources/clusters \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  | jq -r '.[] | select(.location == "eu-frankfurt-1") | .id')
H1_ID=$(curl -s https://api.sevalla.com/v3/resources/process-resource-types \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  | jq -r '.[] | select(.name == "h1") | .id')
```

If a variable ends up empty or `null`, run its `curl` command without `jq`: the response's `message` says what went wrong.

**1. Create the application**

[Create application](https://api-docs.sevalla.com/v3/applications/create-application) (`createApplicationV3`). It creates the web process and doesn't deploy:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
APP_ID=$(curl -s -X POST https://api.sevalla.com/v3/applications \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  -H "Content-Type: application/json" \
  -d @- <<EOF | jq -r .id
{
  "display_name": "quote-tool",
  "cluster_id": "$CLUSTER_ID",
  "source": "privateGit",
  "git_type": "github",
  "repo_url": "https://github.com/acme/quote-tool",
  "default_branch": "main"
}
EOF
)
```

CLI: `sevalla apps create --name quote-tool --cluster "$CLUSTER_ID" --source privateGit --git-type github --repository https://github.com/acme/quote-tool --branch main`. MCP: `create_application` with `deploy` set to `false`; by default it starts a deployment before the size and the probe are set.

**2. Set the size and the readiness probe**

The API creates the web process at **S1**, not the dashboard's **H1**, and without health checks. [List processes](https://api-docs.sevalla.com/v3/applications/list-processes) (`listProcessesV3`) finds the web process, and [Update process](https://api-docs.sevalla.com/v3/applications/update-process) (`updateProcessV3`) sets **H1** and the readiness probe from [Add a health check](#add-a-health-check). Send every probe field, `timeoutSeconds` included: a probe set through the API without it waits up to 120 seconds for an answer, not the dashboard's 1 second. The values are the dashboard's defaults:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
WEB_ID=$(curl -s https://api.sevalla.com/v3/applications/$APP_ID/processes \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  | jq -r '.data[] | select(.type == "web") | .id')
curl -s -X PATCH https://api.sevalla.com/v3/applications/$APP_ID/processes/$WEB_ID \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  -H "Content-Type: application/json" \
  -d @- <<EOF | jq -c '{size: .resource_type_name, readiness: .readiness_probe.httpGet.path, timeout: .readiness_probe.timeoutSeconds}'
{
  "resource_type_id": "$H1_ID",
  "readiness_probe": {
    "httpGet": {"path": "/", "port": 8080, "scheme": "HTTP"},
    "initialDelaySeconds": 0,
    "periodSeconds": 10,
    "timeoutSeconds": 1,
    "successThreshold": 1,
    "failureThreshold": 3
  }
}
EOF
```

It prints `{"size":"h1","readiness":"/","timeout":1}`. The process keeps 1 instance, which [Keep the web process at one instance](#keep-the-web-process-at-one-instance) explains. CLI: `sevalla apps processes update "$WEB_ID" --app-id "$APP_ID" --resource-type "$H1_ID"` sets the size; the CLI can't set probes. MCP: `sevalla_api_update` with `updateProcessV3` and the same body.

**3. Deploy**

[Trigger deployment](https://api-docs.sevalla.com/v3/applications/trigger-deployment) (`triggerDeploymentV3`) deploys the latest commit on the branch:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
DEPLOYMENT_ID=$(curl -s -X POST https://api.sevalla.com/v3/applications/$APP_ID/deployments \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}' | jq -r .id)
```

CLI: `sevalla apps deployments trigger "$APP_ID"`. MCP: `trigger_deployment`.

**4. Wait for the deployment**

[Get deployment](https://api-docs.sevalla.com/v3/applications/get-deployment) (`getDeploymentV3`) until `status` is `success` or `failed`:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
for i in $(seq 1 120); do
  STATUS=$(curl -s "https://api.sevalla.com/v3/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"
```

It prints `success` when the deployment finishes, or `failed`; after 30 minutes without either, it prints the status it last read. On `failed`, read the build log with [Get application deployment logs](https://api-docs.sevalla.com/v3/applications/get-application-deployment-logs) (`getApplicationDeploymentLogsV3`), as in [Verify the deployment](#verify-the-deployment). CLI: `sevalla apps deployments get "$DEPLOYMENT_ID" --app-id "$APP_ID"`. MCP: `get_deployment`, then `get_deployment_logs`.

**5. Turn on auto-deploy**

[Update application](https://api-docs.sevalla.com/v3/applications/update-application) (`updateApplicationV3`):

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
curl -s -X PATCH https://api.sevalla.com/v3/applications/$APP_ID \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"auto_deploy": true}' | jq -r .auto_deploy
```

It prints `true`. CLI: `sevalla apps update "$APP_ID" --data '{"auto_deploy": true}'`. MCP: `sevalla_api_update` with `updateApplicationV3` and the same body.

[List application domains](https://api-docs.sevalla.com/v3/applications/list-application-domains) (`listApplicationDomainsV3`) returns the domain to run the checks in [Verify the deployment](#verify-the-deployment) against:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
APP_HOST=$(curl -s https://api.sevalla.com/v3/applications/$APP_ID/domains \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  | jq -r '.data[] | select(.is_primary) | .name')
curl -s -o /dev/null -w "%{http_code}\n" https://$APP_HOST/
```

It prints `200`. To build from the [Dockerfile](#deploy-with-a-dockerfile) instead, send `{"build_type": "dockerfile", "dockerfile_path": "Dockerfile", "docker_context": "."}` to [Update application](https://api-docs.sevalla.com/v3/applications/update-application), then deploy and wait again. CLI: `sevalla apps update "$APP_ID" --build-type dockerfile --dockerfile-path Dockerfile`.


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