> ## 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 an Eleventy site

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

An Eleventy site builds to static files: `eleventy` turns each Markdown or template file into an HTML page in the `_site` 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 `_site` with Caddy.

## At a glance

| | |
| - | - |
| Source | A Git repository |
| Build | Static site: `npm run build` on Node.js **Latest LTS**, publishing `_site`. 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 `_site` |
| 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. Eleventy 3 runs on Node.js 18 or later.

## Create the project

Create a folder, install Eleventy, and add a `build` script that runs it, as in Eleventy's [Get started](https://www.11ty.dev/docs/):

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
mkdir storefront
cd storefront
npm init -y
npm pkg set type="module"
npm install @11ty/eleventy
npm pkg set scripts.build="eleventy"
```

Add a layout that every page uses:

```html _includes/base.njk theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>{{ title }} | Storefront</title>
</head>
<body>
  <nav><a href="/">Home</a> <a href="/about/">About</a></nav>
  <main>
    {{ content | safe }}
  </main>
</body>
</html>
```

Then a home page, an about page and a 404 page. Eleventy writes `index.md` to `_site/index.html`, `about.md` to `_site/about/index.html`, and `404.md` to `_site/404.html` because of its `permalink`:

```markdown index.md theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
---
layout: base.njk
title: Home
---

# Storefront

Storefront's site runs on Sevalla.
```

```markdown about.md theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
---
layout: base.njk
title: About
---

# About Storefront

Eleventy builds this page from `about.md`.
```

```markdown 404.md theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
---
layout: base.njk
title: Page not found
permalink: 404.html
---

# Page not found

[Go to the home page](/).
```

Keep the dependencies and Eleventy's output out of the repository:

```text .gitignore theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
node_modules/
_site/
```

## Prepare the project for Sevalla

Pin the Node.js version for Ingot:

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

| File | Why Sevalla needs it |
| - | - |
| `package.json` `scripts.build` | The static site's **Build command** and Ingot both run it. |
| `package-lock.json` | Every build installs the same Eleventy version, 3.1.7 at the time of writing. |
| `package.json` `engines.node` | Ingot builds on the Node.js line it names. Without it, Ingot picks its default line, Node.js 24 today, which a new Ingot release can move. See [Pin the Node.js version](https://docs.sevalla.com/guides/javascript/overview.md#pin-the-node-js-version). |

Run `npx @11ty/eleventy --serve` and open `http://localhost:8080` to check the site 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 site"
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`. Eleventy builds to `_site`, 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** | `_site` |

**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 npm only warns that `24.x` doesn't match. 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 build log lists the pages Eleventy wrote, ending with `[11ty] Wrote 3 files`. The site's **Status** on the **Overview** tab changes to **Live**.
2. On the **Overview** tab, click **Visit site**. The home page opens on its `*.sevalla.page` domain, and **About** opens `/about/`.
3. From a terminal, with your site's domain:

   ```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
   curl -s https://storefront-site-x7k2m.sevalla.page/ | grep -o '<title>[^<]*</title>'
   curl -s https://storefront-site-x7k2m.sevalla.page/about/ | grep -o '<title>[^<]*</title>'
   ```

   ```text theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
   <title>Home | Storefront</title>
   <title>About | Storefront</title>
   ```

   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 your `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](#troubleshoot).

## 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 Eleventy from `package.json`, runs the `build` script and serves the `_site` folder with Caddy. 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): Eleventy from `package.json`, built on `node:24-trixie` and served from an `alpine:3` image with Caddy.

   ```text Build log theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
   ╭─ ■ Eleventy · Node.js 24 ──────────────────────────────╮
   │ Node.js          24        package.json engines "24.x" │
   │ Package manager  npm       package-lock.json           │
   │ Framework        Eleventy  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 home page opens. From a terminal:

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

   ```text theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
   <title>About | Storefront</title>
   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 `_site` on the port in `PORT`, answers missing pages with your `404.html` and status `404`, and redirects folder URLs without a trailing slash, such as `/about`, to `/about/` with status `308`.

If the deployment fails, read its build log, then see [Troubleshoot](#troubleshoot) and [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/_site /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
	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 Eleventy. 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 a page doesn't reinstall Eleventy. |
| `handle_errors 404` | Answers missing pages with `404.html` and status `404`. |
| `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
node_modules
_site
.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 commands in [Verify the application](#verify-the-application) print the same output. 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 `www.example.com`, add the domain on the static site's **Domains** tab and create the DNS records that Sevalla shows. 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).

## Troubleshoot

| Symptom | Cause | Fix |
| - | - | - |
| The static site's build log shows `[11ty] Wrote 3 files` and `✅ Build command succeeded`, then the deployment fails with `❌ 'dist' directory does not exist.` | The **Publish directory** is `dist`, which Sevalla fills in for any project with a `build` script, and Eleventy builds to `_site` | Set the **Publish directory** to `_site` in the [build settings](https://docs.sevalla.com/static-sites/build-settings.md#update-the-build-settings) and deploy again |

For more causes, see [Troubleshoot static sites](https://docs.sevalla.com/static-sites/troubleshooting.md) and [Troubleshoot Ingot builds](https://docs.sevalla.com/applications/builds/ingot.md#troubleshoot-ingot-builds).

## 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 [Prepare the project for Sevalla](#prepare-the-project-for-sevalla).
* 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-site",
  "source": "privateGit",
  "git_type": "github",
  "repo_url": "https://github.com/acme/storefront",
  "default_branch": "main",
  "build_command": "npm run build",
  "published_directory": "_site",
  "node_version": "lts",
  "auto_deploy": true
}
JSON
)
```

CLI: `sevalla static-sites create --name storefront-site --repository https://github.com/acme/storefront --branch main --source privateGit --git-type github --build-command "npm run build" --publish-directory _site --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`. Right after a deployment reports `success`, Cloudflare's edge can answer with Sevalla's 404 page for a short time, so the loop waits up to 2 minutes for `200` before the check:

```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)
for i in $(seq 1 12); do
  [ "$(curl -s -o /dev/null -w '%{http_code}' $SITE_URL/about/)" = 200 ] && break
  sleep 10
done
curl -s $SITE_URL/about/ | grep -o '<title>[^<]*</title>'
```

```text theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
<title>About | Storefront</title>
```

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

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

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

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

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

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

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. Right after a deployment reports `success`, the domain can answer `503` for a few seconds while traffic moves to the new instance, so the loop waits up to 1 minute for `200` before the checks:

```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')
for i in $(seq 1 12); do
  [ "$(curl -s -o /dev/null -w '%{http_code}' $APP_URL/)" = 200 ] && break
  sleep 5
done
curl -s $APP_URL/about/ | grep -o '<title>[^<]*</title>'
curl -s -o /dev/null -w '%{http_code}\n' $APP_URL/no-such-page
```

After the build log, the output is `<title>About | Storefront</title>`, 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.