> ## 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.

# Use PostgreSQL from Node.js with Prisma

> Connect a Node.js application on Sevalla to PostgreSQL with Prisma ORM 7: DATABASE_URL over an internal connection, the client generated in the build, and migrations in a job.

A Node.js application on Sevalla reaches a Sevalla PostgreSQL database over an internal connection, and Prisma ORM reads the database's address from the `DATABASE_URL` environment variable. The build generates the Prisma client from your schema, and a job applies your migrations with `prisma migrate deploy` before each deployment, so new code never runs against an old schema. This guide adds a `Product` table to an Express application with Prisma ORM 7.

## At a glance

What this guide adds to the [Express application](https://docs.sevalla.com/guides/javascript/express.md#at-a-glance):

| | |
| - | - |
| Build | `npm run build` runs `prisma generate` during the [Ingot](https://docs.sevalla.com/applications/builds/ingot.md) build, or in the [Dockerfile](#deploy-with-a-dockerfile) |
| Processes | `Migrate` job (`npx prisma migrate deploy`, **Before deployment**) |
| Services | PostgreSQL 18 over an internal connection |
| Size | **H1** for the `Migrate` job, **DB1** for the database |

### Environment variables

| Variable | Required | Available during | Secret | Value |
| - | - | - | - | - |
| `DATABASE_URL` | Yes | Runtime | Yes | `DB_URL` from the internal connection, renamed: `postgres://<user>:<password>@<host>:<port>/<database>` |

The Express application's own variables are in [its table](https://docs.sevalla.com/guides/javascript/express.md#environment-variables). The internal connection also adds `DB_HOST`, `DB_PORT`, `DB_DATABASE`, `DB_USERNAME` and `DB_PASSWORD`, which Prisma doesn't read.

## Before you begin

* An Express application deployed on Sevalla, as in [Deploy Express](https://docs.sevalla.com/guides/javascript/express.md). It pins Node.js 24; Prisma ORM 7 needs Node.js 20.19, 22.12, or 24.0 and later.
* PostgreSQL on your computer, to create migrations against. With Docker:

  ```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
  docker run -d --name storefront-db -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=storefront -p 5432:5432 postgres:18
  ```

## Create the database

**1. Create the database**

In the sidebar, click **Databases**, then click **Create database**. Under **Database**, choose **PostgreSQL** and the newest version. Enter `orders-db` as the **Display name**, choose the same **Data center** as the application and a **Size**, then click **Create database**. See [Create a database](https://docs.sevalla.com/databases/create.md) for every field.

**2. Connect it to the application**

In the sidebar, click **Applications**, then click the application. Click the **Networking** tab. Under **Connected services**, click **Add internal connection**. Choose `orders-db` as the **Service** and turn on **Add environment variables to the application**. In the list of variables, change the key `DB_URL` to `DATABASE_URL`, then click **Add internal connection**.

`DATABASE_URL` holds the database's internal address, `postgres://<user>:<password>@<host>:<port>/<database>`, which Prisma reads as is. The connection also adds `DB_HOST`, `DB_PORT`, `DB_DATABASE`, `DB_USERNAME` and `DB_PASSWORD`, which this guide doesn't use. The variables apply at the next deployment. See [Connect an application](https://docs.sevalla.com/databases/connect.md#connect-an-application).

## Install Prisma

In the project, install Prisma ORM 7 and create its files:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
npm install @prisma/client@7 @prisma/adapter-pg@7 prisma@7 dotenv
npx prisma init --datasource-provider postgresql --output ../generated/prisma --no-skills
```

| Package | What it does |
| - | - |
| `@prisma/client` | The runtime of the generated client |
| `@prisma/adapter-pg` | Connects the client to PostgreSQL through node-postgres. Prisma ORM 7 needs a driver adapter. |
| `prisma` | The CLI that generates the client and applies migrations |
| `dotenv` | Loads `.env` into the CLI on your computer |

Install `prisma` and `dotenv` as dependencies, not dev dependencies: the migration job runs the CLI from the application's image. With both as dependencies, Ingot and the [Dockerfile](#deploy-with-a-dockerfile) can remove every dev dependency after the build. `@7` keeps every Prisma package on the same major version.

`prisma init` creates:

* `prisma/schema.prisma`, the schema, with a generator that writes the client to `generated/prisma`.
* `prisma7.config.ts`, the CLI's configuration, which loads `.env` and reads the database address from `DATABASE_URL`.
* `.env`, with a placeholder `DATABASE_URL`.
* A `/generated/prisma` line in `.gitignore`, so the generated client stays out of the repository.

Keep `.env` out of the repository too, then point `DATABASE_URL` in it at the database on your computer. Sevalla doesn't read this file: the application gets `DATABASE_URL` from the internal connection.

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
echo ".env" >> .gitignore
```

```bash .env theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/storefront"
```

## Define the schema

Add a model to the end of `prisma/schema.prisma`:

```prisma prisma/schema.prisma theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
model Product {
  id        Int      @id @default(autoincrement())
  name      String
  createdAt DateTime @default(now())
}
```

Create the first migration and apply it to the database on your computer:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
npx prisma migrate dev --name init
```

Prisma writes the SQL to `prisma/migrations/<timestamp>_init/migration.sql`. Commit the `prisma/migrations` folder: the migration job applies what's in it.

## Generate the client during the build

The generated client isn't in the repository, so the build creates it. Make `prisma generate` the `build` script, then run it once on your computer:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
npm pkg set scripts.build="prisma generate"
npm run build
```

[Ingot](https://docs.sevalla.com/applications/builds/ingot.md) runs the `build` script during every build, after it installs the dependencies, so the client always matches the committed schema. `prisma generate` doesn't connect to the database.

## Query the database

Create one client for the whole application:

```js db.js theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
import { PrismaPg } from "@prisma/adapter-pg";
import { PrismaClient } from "./generated/prisma/client.ts";

const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });

export const prisma = new PrismaClient({ adapter });
```

The generated client is TypeScript. Node.js 24 runs `.ts` files outside `node_modules` by stripping their types, so the project needs no compile step.

Add routes to `server.js` that list and create products:

```js server.js theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
import express from "express";
import { prisma } from "./db.js";

const app = express();
const port = process.env.PORT || 3000;

app.use(express.json());

app.get("/products", async (req, res) => {
  const products = await prisma.product.findMany({ orderBy: { id: "asc" } });
  res.json(products);
});

app.post("/products", async (req, res) => {
  const product = await prisma.product.create({ data: { name: req.body.name } });
  res.status(201).json(product);
});

const server = app.listen(port, () => {
  console.log(`Listening on port ${port}`);
});

// Finish open requests and close the database connections when Sevalla stops the instance.
process.on("SIGTERM", () => {
  server.close(async () => {
    await prisma.$disconnect();
    process.exit(0);
  });
});
```

To try it on your computer, start the server with the variables from `.env`, then create a product:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
node --env-file=.env server.js
curl -X POST http://localhost:3000/products -H "Content-Type: application/json" -d '{"name":"Coffee mug"}'
```

The response is the new row, such as `{"id":1,"name":"Coffee mug","createdAt":"2026-10-29T18:05:24.848Z"}`.

## Run migrations before each deployment

A [job](https://docs.sevalla.com/applications/processes.md#jobs) with the start policy **Before deployment** runs once per deployment, after the build and before the new version gets traffic:

**1. Create the job**

On the application's **Processes** tab, click **Create process**, then choose **Job**.

**2. Set its command**

Enter `Migrate` as the **Name** and `npx prisma migrate deploy` as the **Custom start command**. Keep the **Start policy** at **Before deployment** and the **Resource** at **H1**, then click **Create process**.

![The Create process sheet with Job selected, the Name and Custom start command fields, the Start policy set to Before deployment, and the H1 resource](https://assets.sevalla.com/screenshots/light/app-processes-job-create.png)

`prisma migrate deploy` applies the migrations in `prisma/migrations` that the database doesn't have yet, and never resets data or creates migrations, unlike `prisma migrate dev`. If the job fails, the deployment fails and the previous version keeps serving traffic. Ingot recommends this job for any project with a `prisma/migrations` folder; [Get Dockerfile](https://api-docs.sevalla.com/v3/applications/get-dockerfile) lists it in `processes`.

## Deploy and verify

Commit and push the changes, `package-lock.json` and `prisma/migrations` included. With auto-deploy on, the push starts a deployment; otherwise, start one with **Deploy now** on the application's **Overview** tab. When the deployment's status changes to **Deployed**:

1. Open the deployment on the **Deployments** tab. Its build log shows the client being generated, for example `✔ Generated Prisma Client (7.10.1) to ./generated/prisma in 41ms`, and, during the rollout, `Migrate has been successful`. On the **Logs** tab, the `Migrate` process's output ends with `All migrations have been successfully applied.`
2. Create a product and list the products. Use your application's domain instead of `storefront-x7k2m.sevalla.app`:

   ```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
   curl -X POST https://storefront-x7k2m.sevalla.app/products -H "Content-Type: application/json" -d '{"name":"Coffee mug"}'
   curl https://storefront-x7k2m.sevalla.app/products
   ```

   The second command returns `[{"id":1,"name":"Coffee mug","createdAt":"..."}]`.
3. On the database's **SQL studio** tab, the `Product` table has the row. See [Database studio](https://docs.sevalla.com/databases/studio.md).

If a request returns `500` or the job fails, read the application's [runtime logs](https://docs.sevalla.com/applications/logs.md):

| Log message | Cause | Fix |
| - | - | - |
| ``The table `public.Product` does not exist in the current database.`` | No migration ran: the deployment has no `Migrate` job | Create the job in [Run migrations before each deployment](#run-migrations-before-each-deployment), then deploy. |
| `Can't reach database server at 127.0.0.1:5432` | `DATABASE_URL` isn't set in the running application | Rename `DB_URL` to `DATABASE_URL` on the **Environment variables** tab, or add the internal connection with its variables, then deploy. |
| `The datasource.url property is required in your Prisma config file when using prisma migrate deploy.` | The job runs without `DATABASE_URL` | The same fix: set `DATABASE_URL`, then deploy. |
| `Cannot find module '/app/generated/prisma/client.ts' imported from /app/db.js` | The build didn't generate the client | Set the `build` script to `prisma generate`, then deploy. |

## Size the connection pool

Each `PrismaClient` keeps a pool of up to 10 connections, node-postgres's default, in every instance of every process that creates one. The database accepts a limited number of connections: run `SHOW max_connections;` in the database's **SQL studio** to see its limit. PostgreSQL keeps 3 of them for administration (`superuser_reserved_connections`), so with a limit of 100, all processes together can open 97.

Add up the pools before you scale: a web process with 4 instances and a background worker with 2 open up to 60 connections, plus 1 for the `Migrate` job and any you open from your computer. When the total gets close to the limit, lower the pool size per instance with `max`:

```js db.js theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL, max: 5 });
```

With [autoscaling](https://docs.sevalla.com/applications/scaling.md#horizontal-autoscaling), count the maximum instance count. When every connection your application can open is in use, new connections fail with `remaining connection slots are reserved for roles with the SUPERUSER attribute`.

## 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 node:24-trixie-slim AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build && npm prune --omit=dev

FROM node:24-trixie-slim
WORKDIR /app
ENV NODE_ENV=production PORT=8080 NPM_CONFIG_UPDATE_NOTIFIER=false
COPY --from=build /app ./
USER 1000:1000
EXPOSE 8080
CMD ["node", "server.js"]
```

| Line or choice | Why |
| - | - |
| `node:24-trixie-slim` | The official Node.js 24 image on Debian 13, the line `engines.node` pins. The tag follows Node.js 24's patch releases, so each build gets its security fixes; a digest would stay on one release. It has the OpenSSL 3 library that Prisma's migration engine needs. |
| Two stages | `npm ci` and the npm cache stay in the `build` stage. The final stage gets the application, its production dependencies and the generated client. |
| `package.json` and `package-lock.json` first | Docker reuses the `npm ci` layer while the lock file doesn't change, so a code change skips the install. |
| `npm run build` | Runs `prisma generate`, which writes the client to `generated/prisma`. |
| `npm prune --omit=dev` | Keeps dev dependencies out of the image. `prisma` and `dotenv` stay, because they're dependencies. |
| `USER 1000:1000` | The server runs as the image's unprivileged `node` user. The application stays owned by root, so the server can read it but not change it. |
| No init process | `server.js` handles `SIGTERM` itself, so Node.js shuts down cleanly as the container's first process. |
| `PORT=8080` and `EXPOSE 8080` | `server.js` listens on `PORT`, which Sevalla sets at runtime. The image's default matches Sevalla's, for running it on your computer. `NPM_CONFIG_UPDATE_NOTIFIER=false` stops `npx` in the `Migrate` job from checking for a new npm. |

What Prisma needs in the image: the generated client in `generated/prisma`; `@prisma/client` and `@prisma/adapter-pg`, which run queries without a native engine; and, for the `Migrate` job, the `prisma` CLI with the schema engine that `npm ci` downloads for Debian with OpenSSL 3, plus `prisma/`, `prisma7.config.ts` and `dotenv`. The job runs `npx prisma migrate deploy` from the same image.

```text .dockerignore theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
.git
node_modules
generated
.env*
npm-debug.log*
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, and its build plan lists them in an `Ignored` row. 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 build log starts with `📦 Building and pushing docker image using Dockerfile...`, then shows each step of your Dockerfile and, during the rollout, `Migrate has been successful`. `curl https://storefront-x7k2m.sevalla.app/products` returns the same products as before, and the deployment's **Dockerfile** tab shows your file. See [Dockerfile](https://docs.sevalla.com/applications/builds/dockerfile.md).

## Deploy changes

To change the schema, edit `prisma/schema.prisma`, run `npx prisma migrate dev --name <change>` on your computer, and commit the new folder in `prisma/migrations` with the code that uses it. The next deployment generates the new client during the build, and the `Migrate` job applies the migration before the new code starts. Make each migration work with the code that's still running until the rollout ends: add a column before the code reads it, and drop it in a later deployment, after no code uses it. See [Deployments](https://docs.sevalla.com/applications/deployments.md).

## Deploy with the API

This sequence adds the database and the migration job of this guide to the application from [Deploy Express](https://docs.sevalla.com/guides/javascript/express.md#deploy-with-the-api) through the [Sevalla API](https://docs.sevalla.com/developer-tools/api.md), so an agent or a script can run it as is. `APP_ID` is that application, and the changes on this page are pushed to its branch. 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`), and `curl`, `jq` and `openssl`. A call that fails returns `status` and `message` instead of the resource, and `jq -r .id` prints `null`: stop and read the message.

**1. Look up the location and the database size**

[Get application](https://api-docs.sevalla.com/v3/applications/get-application) (`getApplicationV3`) returns the application's location in `cluster_id`; the database must be in the same one. [List database resource types](https://api-docs.sevalla.com/v3/resources/list-database-resource-types) (`listDatabaseResourceTypesV3`) returns the database sizes; **DB1** is the smallest.

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
CLUSTER_ID=$(curl -s https://api.sevalla.com/v3/applications/$APP_ID \
  -H "Authorization: Bearer $SEVALLA_API_KEY" | jq -r .cluster_id)
DB_SIZE_ID=$(curl -s https://api.sevalla.com/v3/resources/database-resource-types \
  -H "Authorization: Bearer $SEVALLA_API_KEY" | jq -r '.[] | select(.name == "db1") | .id')
```

CLI: `sevalla apps get "$APP_ID"` and `sevalla resources db-types`. MCP: `get_application` and `get_platform_options`.

**2. Create the database**

[Create database](https://api-docs.sevalla.com/v3/databases/create-database) (`createDatabaseV3`) starts creating a PostgreSQL 18 database with a password you generate. [List database versions](https://api-docs.sevalla.com/v3/resources/list-database-versions) (`listDatabaseVersionsV3`) lists the versions of a location.

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
DB_PASSWORD=$(openssl rand -hex 24)
DB_ID=$(curl -s https://api.sevalla.com/v3/databases \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  -H "Content-Type: application/json" \
  -d @- <<JSON | jq -r .id
{
  "display_name": "orders-db",
  "type": "postgresql",
  "version": "18",
  "cluster_id": "$CLUSTER_ID",
  "resource_type_id": "$DB_SIZE_ID",
  "db_name": "storefront",
  "db_user": "storefront",
  "db_password": "$DB_PASSWORD"
}
JSON
)
```

CLI: `sevalla databases create --name orders-db --type postgresql --db-version 18 --cluster "$CLUSTER_ID" --resource-type "$DB_SIZE_ID" --db-name storefront --db-user storefront --db-password "$DB_PASSWORD"`. MCP: `create_database`, which generates the password and doesn't return it; with `connect_to_application_id` and `env_var_name` set to `DATABASE_URL`, it also connects the database and stores the connection URL, which replaces the next three steps as `postgresql://...`, which Prisma reads like `postgres://`.

**3. Connect the database**

[Create internal connection](https://api-docs.sevalla.com/v3/databases/create-internal-connection) (`createDatabaseInternalConnectionV3`) connects the database to the application over the private network. Unlike the dashboard, it adds no environment variables: a later step adds them.

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
curl -s https://api.sevalla.com/v3/databases/$DB_ID/internal-connections \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"target_id\": \"$APP_ID\", \"target_type\": \"app\"}" | jq .
```

CLI: `sevalla databases connections internal-create "$DB_ID" --target-id "$APP_ID" --target-type app`. MCP: `sevalla_api_create` with `createDatabaseInternalConnectionV3`.

**4. Read the database details**

[Get database](https://api-docs.sevalla.com/v3/databases/get-database) (`getDatabaseV3`) returns the database's internal hostname and port. The loop waits until its `status` is `ready`.

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
for i in $(seq 1 60); do
  [ "$(curl -s https://api.sevalla.com/v3/databases/$DB_ID -H "Authorization: Bearer $SEVALLA_API_KEY" | jq -r '.status // empty')" = ready ] && break
  sleep 10
done
DB=$(curl -s https://api.sevalla.com/v3/databases/$DB_ID -H "Authorization: Bearer $SEVALLA_API_KEY")
```

CLI: `sevalla databases get "$DB_ID" --json`. MCP: `get_database`.

**5. Add DATABASE\_URL**

[Create environment variable](https://api-docs.sevalla.com/v3/applications/create-environment-variable) (`createEnvironmentVariableV3`) adds `DATABASE_URL` in the format the dashboard uses, available during runtime only: `prisma generate` doesn't need it during the build.

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
curl -s https://api.sevalla.com/v3/applications/$APP_ID/env-vars \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --arg value "postgres://$(jq -r .db_user <<< "$DB"):$DB_PASSWORD@$(jq -r .internal_hostname <<< "$DB"):$(jq -r .internal_port <<< "$DB")/$(jq -r .db_name <<< "$DB")" \
    '{key: "DATABASE_URL", value: $value, is_runtime: true, is_buildtime: false}')" | jq -r .key
```

CLI: `sevalla apps env-vars create "$APP_ID" --key DATABASE_URL --value "postgres://..."`; the CLI makes it available during the build too. MCP: `set_environment_variables` with `runtime` `true` and `buildtime` `false`.

**6. Create the migration job**

[List process resource types](https://api-docs.sevalla.com/v3/resources/list-process-resource-types) (`listProcessResourceTypesV3`) returns the **H1** size, and [Create process](https://api-docs.sevalla.com/v3/applications/create-process) (`createProcessV3`) adds the `Migrate` job. A job needs `scaling_strategy` and `job_start_policy`; `beforeDeployment` is the dashboard's **Before deployment**.

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
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')
curl -s https://api.sevalla.com/v3/applications/$APP_ID/processes \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  -H "Content-Type: application/json" \
  -d @- <<JSON | jq -r .display_name
{
  "display_name": "Migrate",
  "type": "job",
  "entrypoint": "npx prisma migrate deploy",
  "resource_type_id": "$H1_ID",
  "scaling_strategy": {"type": "manual", "config": {"instanceCount": 1}},
  "job_start_policy": "beforeDeployment"
}
JSON
```

CLI: `sevalla apps processes create "$APP_ID" --name Migrate --type job --resource-type "$H1_ID" --command "npx prisma migrate deploy" --job-start-policy beforeDeployment`. MCP: `sevalla_api_create` with `createProcessV3` and the same body.

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

**8. 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`.

**9. Check the deployment**

[Get application deployment logs](https://api-docs.sevalla.com/v3/applications/get-application-deployment-logs) (`getApplicationDeploymentLogsV3`) returns the build log, [Get application runtime logs](https://api-docs.sevalla.com/v3/applications/get-application-runtime-logs) (`getApplicationRuntimeLogsV3`) what the `Migrate` job printed, and [List application domains](https://api-docs.sevalla.com/v3/applications/list-application-domains) (`listApplicationDomainsV3`) the application's domain:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
curl -s -G "https://api.sevalla.com/v3/applications/$APP_ID/deployments/$DEPLOYMENT_ID/logs?limit=5000" --data-urlencode "search=Migrate has been" \
  -H "Authorization: Bearer $SEVALLA_API_KEY" | jq -r '.[].message'
curl -s -G https://api.sevalla.com/v3/applications/$APP_ID/runtime-logs --data-urlencode "search=migrations have been" \
  -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 -w '\n' -X POST $APP_URL/products -H "Content-Type: application/json" -d '{"name":"Coffee mug"}'
curl -s -w '\n' $APP_URL/products
```

The output, with the time of your request in `createdAt`:

```text theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
✅ Migrate has been successful
All migrations have been successfully applied.
{"id":1,"name":"Coffee mug","createdAt":"2026-10-30T05:12:46.070Z"}
[{"id":1,"name":"Coffee mug","createdAt":"2026-10-30T05:12:46.070Z"}]
```

CLI: `sevalla apps deployments logs "$DEPLOYMENT_ID" --app-id "$APP_ID" --search "Migrate has been"`, `sevalla apps logs runtime "$APP_ID" --search "migrations have been"` and `sevalla apps domains list --app-id "$APP_ID"`. MCP: `get_deployment_logs`, `search_runtime_logs` and `get_application`.


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