> ## 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 Docusaurus site

> Deploy a Docusaurus 3 site to Sevalla as a free static site, or as an application that Ingot or a Dockerfile builds and serves with Caddy.

A Docusaurus site builds to static files: `docusaurus build` writes every page as HTML to the `build` folder. Sevalla publishes that folder as a static site, served from Cloudflare's edge and free up to 100 GB of bandwidth a month. When the site has to run as an [application](#deploy-as-an-application) instead, Ingot, Sevalla's builder, builds it and serves `build` with Caddy.

## At a glance

| | |
| - | - |
| Source | A Git repository |
| Build | Static site: `npm run build` on Node.js **Latest LTS**, publishing `build`. Application: [Ingot](https://docs.sevalla.com/applications/builds/ingot.md) (Node.js 24, npm, Caddy), or the [Dockerfile](#deploy-with-a-dockerfile) |
| Start command | Application: none to set. Caddy serves `build`; Ingot doesn't run the `start` script, `docusaurus start` |
| Port | Application: the `PORT` environment variable, `8080` by default |
| Processes | Application: web process |
| Disk | None |
| Size | Static site: none to choose. Application: **H1** for the web process |

### Environment variables

The site needs no environment variables, as a static site or as an application.

## 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).
* Node.js 24 or later on your computer.

## Create the project

Create a site from Docusaurus's `classic` template with TypeScript:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
npx create-docusaurus@latest storefront classic --typescript --package-manager npm
cd storefront
```

The site comes with a `.gitignore`, docs, a blog, and a `build` script that runs `docusaurus build`. In `docusaurus.config.ts`, set `url` to the address the site will have, such as `https://docs.example.com`: Docusaurus writes it into the sitemap and the page metadata, and the build doesn't need it to be right.

Run `npm run build && npm run serve` and open `http://localhost:3000` to check the build locally. Then commit everything and push it to your Git provider:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
git init
git add .
git commit -m "Create the storefront docs"
git branch -M main
git remote add origin https://github.com/acme/storefront.git
git push -u origin main
```

## Deploy as a static site

**1. Choose the repository**

In the sidebar, click **Static sites**, then click **Create static site**. Under **Source**, click **Private repository**, then choose the **Git provider**, the **Repository** and the **Branch**.

**2. Set the build settings**

Under **Build settings**, Sevalla fills in `npm run build` and `dist` from `package.json`. Docusaurus builds to `build`, so change the **Publish directory**:

| Field | Value |
| - | - |
| **Build site before publishing** | On |
| **Node version** | **Latest LTS** |
| **Build command** | `npm run build` |
| **Root directory** | Empty, for the repository root |
| **Publish directory** | `build` |

**3. Create the static site**

Enter a **Name** under **Details**, then click **Create static site**. Sevalla starts the first deployment right away, and deploys every push to the branch while **Auto-deploy** is on.

Static sites don't read `engines` in `package.json`: the build uses the **Node version** from the build settings, and **Latest LTS** meets Docusaurus 3's requirement of Node.js 20 or later. See [Build settings](https://docs.sevalla.com/static-sites/build-settings.md) for every field.

### Verify the static site

1. On the **Deployments** tab, wait for the deployment's status to change to **Deployed**. The site's **Status** on the **Overview** tab changes to **Live**.
2. On the **Overview** tab, click **Visit site**. The site's home page opens on its `*.sevalla.page` domain, and **Docs** opens `/docs/intro`.
3. From a terminal, with your site's domain:

   ```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
   curl -s https://storefront-docs-x7k2m.sevalla.page/ | grep -o '<title data-rh=true>[^<]*</title>'
   curl -sL -o /dev/null -w '%{http_code}\n' https://storefront-docs-x7k2m.sevalla.page/docs/intro
   ```

   The output is `<title data-rh=true>Hello from My Site | My Site</title>`, the template's home page title, then `200`. With the API, [Get static site deployment](https://api-docs.sevalla.com/v3/static-sites/get-static-site-deployment) returns the deployment's status; see [Deploy with the API](#deploy-with-the-api).

![The static site's Details card with its domain, Live status, source repository and the Visit site button](https://assets.sevalla.com/screenshots/light/static-site-overview-visit.png)

A path with no page returns Sevalla's 404 page. To show Docusaurus's own `404.html` instead, see [Custom 404 pages](https://docs.sevalla.com/static-sites/redirects.md#custom-404-pages). If the deployment fails, open it on the **Deployments** tab and read its build log, then see [Troubleshoot static sites](https://docs.sevalla.com/static-sites/troubleshooting.md).

## Deploy as an application

An application built from the same repository serves the same files. A static site costs nothing up to the monthly allowances, so use an application only when the site needs something only applications have, such as promoting one build through the stages of a [pipeline](https://docs.sevalla.com/pipelines/overview.md).

[Ingot](https://docs.sevalla.com/applications/builds/ingot.md) detects Docusaurus from `package.json`, runs the `build` script and serves the `build` folder with Caddy. It recognizes the template's `start` script, `docusaurus start`, as the development server and doesn't run it. Pin the Node.js version:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
npm pkg set engines.node="24.x"
```

The template's `>=20.0` lets Ingot pick its default line, Node.js 24 today, which a new Ingot release can move. `24.x` keeps the build on Node.js 24. See [Pin the Node.js version](https://docs.sevalla.com/guides/javascript/overview.md#pin-the-node-js-version).

Commit and push the changes, then create the application. These steps build with Ingot. 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 application

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): Docusaurus from `package.json`, built on `node:24-trixie` and served from an `alpine:3` image with Caddy, with no **Start** row: Caddy serves the files, not the `start` script.

   ```text Build log theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
   ╭─ ■ Docusaurus · Node.js 24 ──────────────────────────────╮
   │ Node.js          24          package.json engines "24.x" │
   │ Package manager  npm         package-lock.json           │
   │ Framework        Docusaurus  package.json                │
   ├─ Image ──────────────────────────────────────────────────┤
   │ Build            node:24-trixie                          │
   │ Build            caddy:2-alpine                          │
   │ Runtime          alpine:3                                │
   │ 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**.

3. Click the domain under the application's name, for example `storefront-x7k2m.sevalla.app`. The site's home page opens. From a terminal:

   ```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
   curl -s https://storefront-x7k2m.sevalla.app/ | grep -o '<title data-rh=true>[^<]*</title>'
   curl -s -o /dev/null -w '%{http_code}\n' https://storefront-x7k2m.sevalla.app/no-such-page
   ```

   The output is `<title data-rh=true>Hello from My Site | My Site</title>`, then `404`. With the API, [Get deployment](https://api-docs.sevalla.com/v3/applications/get-deployment) returns the deployment's status and [Get application deployment logs](https://api-docs.sevalla.com/v3/applications/get-application-deployment-logs) its build log; see [Deploy with the API](#deploy-with-the-api).

![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)

Caddy serves `build` on the port in `PORT`, answers missing pages with Docusaurus's `404.html` and status `404`, and marks the content-hashed files under `/assets/` as cacheable for a year. To serve them from Cloudflare's servers, turn on the CDN on the application's **Networking** tab. See [CDN and edge caching](https://docs.sevalla.com/applications/caching.md).

If the deployment fails, read its build log, then see [Troubleshoot Ingot builds](https://docs.sevalla.com/applications/builds/ingot.md#troubleshoot-ingot-builds).

## Deploy with a Dockerfile

Ingot needs no Dockerfile. Write one when you want full control of the image: the base image, the web server and every layer. Add a `Dockerfile`, a `Caddyfile` and a `.dockerignore` to the root of the repository:

```dockerfile Dockerfile theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
FROM node:24-trixie-slim AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM caddy:2-alpine AS caddy
RUN setcap -r /usr/bin/caddy

FROM alpine:3
COPY --from=caddy /usr/bin/caddy /usr/bin/caddy
COPY --from=caddy /etc/mime.types /etc/mime.types
COPY Caddyfile /etc/caddy/Caddyfile
COPY --from=build /app/build /srv
ENV XDG_CONFIG_HOME=/tmp XDG_DATA_HOME=/tmp
USER 1000:1000
EXPOSE 8080
CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]
```

```text Caddyfile theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
{
	admin off
	auto_https off
	persist_config off
}

:{$PORT:8080} {
	root * /srv
	encode zstd gzip
	header /assets/* Cache-Control "public, max-age=31536000, immutable"
	file_server

	handle_errors 404 {
		rewrite * /404.html
		file_server
	}
}
```

| Line or choice | Why |
| - | - |
| `node:24-trixie-slim` | Node.js 24, the line `engines.node` pins, on Debian 13, for the build only. A line tag, not a digest, so each build picks up Node.js and Debian security fixes. |
| Three stages | The `build` stage runs `docusaurus build`. The `caddy` stage takes Caddy from its official image and removes the file capability that lets it bind ports below 1024, which a server running as a regular user on port 8080 doesn't need. The final image is Alpine Linux with Caddy and the site: no Node.js, no `node_modules` and no source. |
| `package.json` and `package-lock.json` before the source | `npm ci` installs exactly what the lock file lists, and the [build cache](https://docs.sevalla.com/applications/builds/overview.md#build-cache) reuses that layer until the lock file changes, so a change to your docs doesn't reinstall the dependencies. |
| `handle_errors 404` | Answers missing pages with Docusaurus's `404.html` and status `404`. Folder URLs such as `/docs/intro` redirect to `/docs/intro/`, which serves `index.html`. |
| `USER 1000:1000` | Caddy runs as a regular user, not root. The copied files stay owned by root, so the server can read them but not change them. Caddy keeps its state in `/tmp`, which `XDG_CONFIG_HOME` and `XDG_DATA_HOME` point to. |
| `:{$PORT:8080}` | Caddy listens on `PORT`, which Sevalla sets to the web process's port, or `8080` when it isn't set. Caddy stops gracefully on `SIGTERM` by itself, so the image needs no init process. |

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

Commit and push the three files. Ingot doesn't use the `Dockerfile` and adds your `.dockerignore` patterns to its own, so you can keep the files in a repository that builds with Ingot. 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 deployment's build log shows `📦 Building and pushing docker image using Dockerfile...` instead of Ingot's build plan, and its **Dockerfile** tab shows your file. When the status changes to **Deployed**, the domain opens the site's home page. See [Dockerfile](https://docs.sevalla.com/applications/builds/dockerfile.md) for build arguments and private base images.

## Deploy changes

With **Auto-deploy** on, every push to the branch starts a deployment of the static site or the application; otherwise, deploy each change from the **Deployments** tab. See [Static site deployments](https://docs.sevalla.com/static-sites/deployments.md) and [Deployments](https://docs.sevalla.com/applications/deployments.md).

## Add a custom domain

To serve the site from your own domain, such as `docs.example.com`, add the domain on the static site's **Domains** tab and create the DNS records that Sevalla shows, then set `url` in `docusaurus.config.ts` to it. Applications have a **Domains** tab too, but custom domains need a web process larger than **H1**. See [Custom domains](https://docs.sevalla.com/domains/overview.md).

## Deploy with the API

This sequence creates the static site from [Deploy as a static site](#deploy-as-a-static-site) and the application from [Deploy as an application](#deploy-as-an-application) through the [Sevalla API](https://docs.sevalla.com/developer-tools/api.md), so an agent or a script can run it as is. Run the first four steps for the static site, the rest for the application. Each step names the operation, shows the `curl` command, and gives the matching [CLI](https://docs.sevalla.com/developer-tools/cli.md) command and [MCP server](https://docs.sevalla.com/ai-agents/overview.md) tool.

You need:

* An [API key](https://docs.sevalla.com/developer-tools/api-keys.md) with the **Company developer** role, in the `SEVALLA_API_KEY` environment variable. The CLI reads the same key from `SEVALLA_API_TOKEN`.
* `curl` and `jq`, and the project pushed to GitHub as in [Create the project](#create-the-project).
* A GitHub account that can read the repository, connected to Sevalla. See [Git providers](https://docs.sevalla.com/git/overview.md). The API can't list connected accounts: when none can read the repository, [Create static site](https://api-docs.sevalla.com/v3/static-sites/create-static-site) and [Create application](https://api-docs.sevalla.com/v3/applications/create-application) fail with `400` and `Cannot access repository https://github.com/acme/storefront: Repository not found or you don't have access.`

A call that fails returns `status` and `message` instead of the resource, and `jq -r .id` prints `null`: stop and read the message.

**1. Create the static site**

[Create static site](https://api-docs.sevalla.com/v3/static-sites/create-static-site) (`createStaticSiteV3`) takes the build settings: `node_version` `lts` is the dashboard's **Latest LTS**, and `published_directory` is the **Publish directory**.

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
SITE_ID=$(curl -s https://api.sevalla.com/v3/static-sites \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  -H "Content-Type: application/json" \
  -d @- <<JSON | jq -r .id
{
  "display_name": "storefront-docs",
  "source": "privateGit",
  "git_type": "github",
  "repo_url": "https://github.com/acme/storefront",
  "default_branch": "main",
  "build_command": "npm run build",
  "published_directory": "build",
  "node_version": "lts",
  "auto_deploy": true
}
JSON
)
```

CLI: `sevalla static-sites create --name storefront-docs --repository https://github.com/acme/storefront --branch main --source privateGit --git-type github --build-command "npm run build" --publish-directory build --node-version lts`. MCP: `create_static_site` with the same fields.

**2. Deploy the static site**

[Deploy static site](https://api-docs.sevalla.com/v3/static-sites/deploy-static-site) (`deployStaticSiteV3`) starts the first deployment. Unlike the dashboard, creating a static site through the API doesn't start one.

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

CLI: `sevalla static-sites deployments trigger "$SITE_ID"`. MCP: `trigger_deployment` with `service_type` `static_site`.

**3. Wait for the static site deployment**

[Get static site deployment](https://api-docs.sevalla.com/v3/static-sites/get-static-site-deployment) (`getStaticSiteDeploymentV3`) returns the deployment's `status`: `waiting`, then `inProgress`, then `success` or `failed`. The loop polls every 15 seconds for up to 30 minutes and keeps polling through a failed request.

```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/static-sites/$SITE_ID/deployments/$SITE_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's `failed`, [Get static site deployment logs](https://api-docs.sevalla.com/v3/static-sites/get-static-site-deployment-logs) (`getStaticSiteDeploymentLogsV3`) returns the build log. CLI: `sevalla static-sites deployments get "$SITE_DEPLOYMENT_ID" --site-id "$SITE_ID"` and `sevalla static-sites deployments logs "$SITE_DEPLOYMENT_ID" --site-id "$SITE_ID"`. MCP: `get_deployment` and `get_deployment_logs` with `service_type` `static_site`.

**4. Check the static site**

[Get static site](https://api-docs.sevalla.com/v3/static-sites/get-static-site) (`getStaticSiteV3`) returns the site's domain in `hostname`:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
SITE_URL=https://$(curl -s https://api.sevalla.com/v3/static-sites/$SITE_ID \
  -H "Authorization: Bearer $SEVALLA_API_KEY" | jq -r .hostname)
curl -s $SITE_URL/ | grep -o '<title data-rh=true>[^<]*</title>'
curl -sL -o /dev/null -w '%{http_code}\n' $SITE_URL/docs/intro
```

The output is the template's home page title, then `200` for the first doc:

```text theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
<title data-rh=true>Hello from My Site | My Site</title>
200
```

CLI: `sevalla static-sites get "$SITE_ID"`. MCP: `get_static_site`.

**5. Look up the location and the size**

[List clusters](https://api-docs.sevalla.com/v3/resources/list-clusters) (`listClustersV3`) returns every location your company can use, with its region ID. This guide uses Frankfurt, Germany, `eu-frankfurt-1`. [List process resource types](https://api-docs.sevalla.com/v3/resources/list-process-resource-types) (`listProcessResourceTypesV3`) returns the process sizes.

```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')
```

CLI: `sevalla resources clusters` and `sevalla resources process-types`. MCP: `get_platform_options`.

**6. Create the application**

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

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

CLI: `sevalla apps create --name storefront --cluster "$CLUSTER_ID" --source privateGit --git-type github --repository https://github.com/acme/storefront --branch main`. MCP: `create_application` with `deploy` set to `false`.

**7. Size the web process**

The API creates the web process at **S1**, while the dashboard creates it at **H1**, which is enough for Caddy. [List processes](https://api-docs.sevalla.com/v3/applications/list-processes) (`listProcessesV3`) returns the web process, and [Update process](https://api-docs.sevalla.com/v3/applications/update-process) (`updateProcessV3`) sets its size. Keep **S1**, and skip this step, to add a custom domain later.

```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 "{\"resource_type_id\": \"$H1_ID\"}" | jq -r .resource_type_name
```

CLI: `sevalla apps processes update "$WEB_ID" --app-id "$APP_ID" --resource-type "$H1_ID"`. MCP: `scale_process` with `size_id` and `apply_now` set to `false`.

**8. Choose the Dockerfile build strategy (optional)**

To build from the `Dockerfile` in [Deploy with a Dockerfile](#deploy-with-a-dockerfile) instead of with Ingot, [Update application](https://api-docs.sevalla.com/v3/applications/update-application) (`updateApplicationV3`) sets the build strategy:

```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 '{"build_type": "dockerfile", "dockerfile_path": "Dockerfile", "docker_context": "."}' | jq -r .build_type
```

CLI: `sevalla apps update "$APP_ID" --data` with the same body. MCP: `sevalla_api_update` with `updateApplicationV3` and the same body.

**9. Turn on auto-deploy**

[Update application](https://api-docs.sevalla.com/v3/applications/update-application) (`updateApplicationV3`) with `auto_deploy` deploys every push to the branch.

```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 .auto_deploy
```

CLI: `sevalla apps update "$APP_ID" --data '{"auto_deploy": true}'`. MCP: `sevalla_api_update` with `updateApplicationV3`.

**10. Deploy**

[Trigger deployment](https://api-docs.sevalla.com/v3/applications/trigger-deployment) (`triggerDeploymentV3`) builds the latest commit of the branch and rolls it out.

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
DEPLOYMENT_ID=$(curl -s 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`.

**11. Wait for the deployment**

[Get deployment](https://api-docs.sevalla.com/v3/applications/get-deployment) (`getDeploymentV3`) returns the deployment's `status`: `waiting`, then `inProgress`, then `success` or `failed`. The loop polls every 15 seconds for up to 30 minutes and keeps polling through a failed request.

```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"
```

CLI: `sevalla apps deployments get "$DEPLOYMENT_ID" --app-id "$APP_ID"`. MCP: `get_deployment`.

**12. Check the application**

[Get application deployment logs](https://api-docs.sevalla.com/v3/applications/get-application-deployment-logs) (`getApplicationDeploymentLogsV3`) returns the build log, which starts with the build plan in [Verify the application](#verify-the-application) and, on a failed deployment, ends with the error. [List application domains](https://api-docs.sevalla.com/v3/applications/list-application-domains) (`listApplicationDomainsV3`) returns the application's domain:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
curl -s "https://api.sevalla.com/v3/applications/$APP_ID/deployments/$DEPLOYMENT_ID/logs?limit=5000" \
  -H "Authorization: Bearer $SEVALLA_API_KEY" | jq -r '.[].message'
APP_URL=https://$(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 $APP_URL/ | grep -o '<title data-rh=true>[^<]*</title>'
curl -sL -o /dev/null -w '%{http_code}\n' $APP_URL/docs/intro
curl -s -o /dev/null -w '%{http_code}\n' $APP_URL/no-such-page
```

After the build log, the output is the same title and `200` as for the static site, then `404` from Caddy for a missing page.

CLI: `sevalla apps deployments logs "$DEPLOYMENT_ID" --app-id "$APP_ID"` and `sevalla apps domains list --app-id "$APP_ID"`. MCP: `get_deployment_logs` and `get_application`.


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