> ## 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 Gleam applications

> How Gleam applications build and run on Sevalla with Ingot: Erlang shipments, the Gleam and Erlang/OTP versions, the PORT variable, schedulers, processes and Dockerfiles.

A Gleam web server runs on Sevalla as an [application](https://docs.sevalla.com/applications/overview.md): Sevalla builds an image from your Git repository, compiles your project to Erlang bytecode with `gleam export erlang-shipment`, and runs it on the Erlang virtual machine (the BEAM) as processes. The web process runs the `main` function of your project, background workers and cron jobs can run other modules of the same project, and jobs run migrations before each deployment.

## Framework guides

> **[Wisp](https://docs.sevalla.com/guides/gleam/wisp.md)**
>
> Deploy a Wisp JSON API on Mist, with a secret key and a graceful shutdown on Erlang/OTP 29.

## How Gleam applications run on Sevalla

| Part | On Sevalla |
| - | - |
| Build | [Ingot](https://docs.sevalla.com/applications/builds/ingot.md), Sevalla's builder, by default. It recognizes a Gleam project by `gleam.toml`, also when the repository has a `package.json`, downloads the packages with `gleam deps download`, then runs `gleam export erlang-shipment`, which compiles the project and its packages into `build/erlang-shipment`. |
| Target | Erlang. A project with `target = "javascript"` in `gleam.toml` fails the build with `gleam.toml targets JavaScript`: Gleam has no way to run a JavaScript-target project outside `gleam run`. |
| Packages | Gleam's package manager, from Hex. Ingot downloads the packages with `gleam.toml` and `manifest.toml` alone, so that layer is reused while only your code changes, unless a dependency is a local path, which needs the whole repository. Commit `manifest.toml`: without it, the build log warns `There is no manifest.toml, so dependency versions are chosen during the build`. |
| Gleam version | The newest Gleam release that Ingot knows, 1.19.2 with Ingot v0.4.1, unless the project asks for another. See [Pin the Gleam version](#pin-the-gleam-version). |
| Erlang/OTP version | The release that the Gleam image compiles with: Erlang/OTP 29 for Gleam 1.19.2. The application runs on `erlang:29-slim`, which gets OTP 29's patch releases on every build. |
| Erlang code | The build image has rebar3 and the compilers that Erlang packages need, such as Mist's `hpack_erl`. Erlang modules (`.erl` files) in `src` compile with the project. |
| Start command | `/app/entrypoint.sh run`, the shipment's script, which runs the `main` function of the module named after the project in `gleam.toml`, such as `src/storefront.gleam` for `name = "storefront"`. To start something else, add a `web:` line to a `Procfile` or set `start` in [ingot.json](https://docs.sevalla.com/applications/builds/ingot.md#configure-the-build). |
| Port | Sevalla sets the `PORT` environment variable to the web process's port, `8080` by default. Read it with a package such as `envoy`, and listen on all network interfaces: Mist listens on `127.0.0.1` unless you call `mist.bind("0.0.0.0")`. See the [Wisp guide](https://docs.sevalla.com/guides/gleam/wisp.md#create-the-project). |
| Concurrency | The BEAM runs each request in its own lightweight process, on one scheduler thread per CPU of the server the instance runs on, not per CPU of its size: for example, 16 schedulers on an **H1** instance, which has 0.3 vCPU. To match the size, add the environment variable `ERL_FLAGS` with the value `+S 1`, which starts 1. To serve more traffic, choose a larger size or add instances. See [Scaling](https://docs.sevalla.com/applications/scaling.md). |
| Shutdown | Sevalla sends `SIGTERM` to stop an instance. The BEAM handles it itself, logging `SIGTERM received - shutting down`, stops the running applications and exits. `entrypoint.sh` replaces itself with the BEAM, which Ingot's image runs under `tini`. |
| Image | Ingot builds in `ghcr.io/gleam-lang/gleam:v1.19.2-erlang` and runs the shipment on `erlang:29-slim`, with CA certificates, time zone data and `LANG=C.UTF-8`, as user `1000:1000`. Gleam itself isn't in the image. |
| Runtime files | The shipment holds each package's `priv` folder: files in your project's `priv` folder are in `/app/<project>/priv`, such as `/app/storefront/priv`. `wisp.priv_directory("storefront")` returns that path. |
| Migrations | A job with the start policy **Before deployment** that runs a module of your project, such as `src/storefront/migrate.gleam`: `exec erl -pa /app/*/ebin -noshell -eval 'storefront@migrate:main()' -s init stop`. Gleam names the Erlang module after the path, with `@` for `/`. If the job fails, the deployment fails and the previous version keeps running. |
| Workers | A background worker that runs another module's `main` function, which doesn't return: `exec erl -pa /app/*/ebin -noshell -eval 'storefront@worker:main()'`. See [Run workers and jobs](https://docs.sevalla.com/applications/builds/ingot.md#run-workers-and-jobs). |
| Scheduled tasks | A cron job that runs a module's `main` function, with `-s init stop` so the BEAM exits when it returns. |
| Clustering | Not available. Instances don't form an Erlang cluster: the web process's internal hostname resolves to one service address, not to each instance. |
| Databases | PostgreSQL, MySQL, MariaDB, Redis or Valkey on Sevalla, reached over an [internal connection](https://docs.sevalla.com/databases/connect.md#connect-an-application) that adds the connection details as environment variables. |
| Files | The filesystem resets on every deployment. Keep uploads in [object storage](https://docs.sevalla.com/object-storage/overview.md), or attach a [disk](https://docs.sevalla.com/applications/disks.md). |
| Dockerfile | Switch the build strategy to [Dockerfile](https://docs.sevalla.com/applications/builds/dockerfile.md) on the application's **Settings** tab. See [Build with a Dockerfile](#build-with-a-dockerfile). |

Every process (web, worker, cron job and job) runs from the same image, each with its own start command. Sevalla runs a custom start command with `/bin/sh -l -c`, which expands `/app/*/ebin` to every package's folder, and `exec` makes the BEAM receive `SIGTERM`. See [Processes](https://docs.sevalla.com/applications/processes.md) for every process type and its settings.

## Pin the Gleam version

Ingot builds with the newest Gleam release it knows, 1.19.2 with Ingot v0.4.1, unless the project asks for another. Gleam publishes its images for exact releases, so a new Ingot release moves the build to the newest Gleam release, and to the Erlang/OTP release that Gleam's image comes with.

Ingot takes the version from the first of these that's set:

1. The `INGOT_GLEAM_VERSION` environment variable, marked **Available during buildtime**, or `versions.gleam` in [ingot.json](https://docs.sevalla.com/applications/builds/ingot.md#configure-the-build).
2. The `gleam` line of `.tool-versions`, then mise's configuration, in the build path or a folder above it up to the repository root.
3. The `gleam` requirement in `gleam.toml`, such as `gleam = ">= 1.19.0"`, which the Gleam compiler also checks. Clauses join with `and`; a requirement with `or` fails the build.

How Ingot reads the version:

* A requirement, such as `>= 1.19.0` in `gleam.toml`, builds with the newest release Ingot knows that meets it. The build plan's **Gleam** row shows `1`, the line, and where the requirement came from.
* An exact release, such as `gleam 1.19.2` in `.tool-versions`, stays on that release, and the build log warns that its image won't receive security rebuilds.
* A release newer than every one Ingot knows fails the build, for example `gleam.toml gleam: no gleam version in this Ingot release satisfies ">= 1.30.0" (newest known: 1.19.2)`.

See [Set the language version](https://docs.sevalla.com/applications/builds/ingot.md#set-the-language-version).

## Build with a Dockerfile

Ingot needs no Dockerfile, but you can write your own for full control of the image. A Dockerfile for Gleam on Sevalla builds in the Gleam team's `ghcr.io/gleam-lang/gleam` image of the release you develop with, copies `gleam.toml` and `manifest.toml` and runs `gleam deps download` before it copies the code, so a code change reuses the downloaded packages, then runs `gleam export erlang-shipment`. The final stage copies only the shipment into the slim official `erlang` image of the same Erlang/OTP release, sets `LANG=C.UTF-8`, runs as a user other than root, and starts `entrypoint.sh`, which replaces itself with the BEAM: it handles `SIGTERM` itself, so it needs no init process. See [Deploy a Wisp application](https://docs.sevalla.com/guides/gleam/wisp.md#deploy-with-a-dockerfile).


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