INGOT_APP environment variable tells Ingot, Sevalla’s builder, which app to build with turbo and start. This guide deploys the web and docs Next.js apps of Turborepo’s starter.
At a glance
Environment variables
Before you begin
- A Sevalla company with a payment method, and a GitHub, GitLab or Bitbucket account for the repository. See Git providers.
- Node.js 24 or later and pnpm on your computer.
Create the project
Create a monorepo from Turborepo’sbasic example:
apps/web and apps/docs, and shared packages in packages/, such as @repo/ui. Each app’s start script runs next start. create-turbo installs the dependencies and creates a Git repository.
The starter pins Next.js 16.3.8, which has security advisories fixed in Next.js 16.4.1. Update both apps to the latest Next.js release:
Prepare the project for Sevalla
Pin the Node.js version in the rootpackage.json:
engines.node to >=24, which lets a new Ingot release move the build to a newer Node.js line. 24.x keeps it on Node.js 24. See Pin the Node.js version.
Build each app as a standalone server. Set output in apps/web/next.config.js, and the same in apps/docs/next.config.js:
output: "standalone", next build copies the server and only the files it needs from the workspace into the app’s .next/standalone folder. The image runs that server, without the workspace’s other apps or its devDependencies, with Ingot and with a Dockerfile alike.
Each Sevalla application builds one app, named by the INGOT_APP environment variable:
INGOT_APP, Ingot, the default build strategy, builds the app from the repository root, as described in Build an application in a monorepo:
- It installs the whole workspace at the root with
pnpm install --frozen-lockfile, using the pnpm release in the root’spackageManagerfield. Commitpnpm-lock.yaml. - Because the repository has a
turbo.json, it builds withturbo run build --filter='web...', which builds thewebapp and the workspace packages it depends on, such as@repo/ui. - It detects the standalone output, copies it into the image and starts the app with
node server.js.
PORT, which Sevalla sets to the web process’s port (8080 by default), so the ports that the dev scripts use don’t matter on Sevalla, and Ingot sets HOSTNAME to 0.0.0.0 so that it listens on all network interfaces. It handles SIGTERM itself: when Sevalla replaces an instance during a deployment, it stops accepting connections and finishes the requests in progress before it exits.
Run pnpm build to check that the monorepo builds locally. Then commit everything and push it to your Git provider:
Deploy to Sevalla
These steps build with Ingot. To build from your own Dockerfiles instead, see Deploy with a Dockerfile.Create the application
storefront-web as the Name. 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.Add the INGOT_APP variable
INGOT_APP as the Key and apps/web as the Value. Keep Available during buildtime selected, because Ingot reads the variable during the build, and click Add environment variables.Deploy
Turn on auto-deploy
Deploy the docs app
storefront-docs, from the same repository and branch, with INGOT_APP set to apps/docs.Verify the deployment
-
On each application’s Deployments tab, click the deployment. Its Build log shows Ingot’s build plan. For
storefront-web, the plan showsapps/webfromINGOT_APP, theturbobuild ofweband the workspace packages it depends on, and the standalone server as the start command:Build log - Wait for the deployment’s status to change to Deployed. The application’s status next to its name changes to Running.
-
Click the domain under each application’s name, for example
storefront-web-x7k2m.sevalla.app. Each one opens its own Next.js starter page, which saysGet started by editing apps/web/app/page.tsxforstorefront-webandapps/docs/app/page.tsxforstorefront-docs.


This workspace has several applications and no start script at its root: apps/docs (docs), apps/web (web) means INGOT_APP is missing or isn’t Available during buildtime.
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. Each app gets its ownDockerfile, which uses turbo prune to build only that app and the workspace packages it depends on. Add this one as apps/web/Dockerfile:
apps/docs/Dockerfile with the same content, with docs in place of every web.
.dockerignore, at the root of the repository:
.dockerignore patterns to its own, and its build plan lists them in an Ignored row. Then switch each application to the Dockerfile build strategy:
Choose the Dockerfile build strategy
storefront-web application’s Settings tab, in the Build strategy section, click Update build strategy. Click Dockerfile, enter apps/web/Dockerfile as the Dockerfile path, keep the Docker context ., then click Update build strategy. The build needs the whole repository as its context, because turbo prune reads the root’s lock file and every workspace package.Deploy
Switch the docs app
storefront-docs, with apps/docs/Dockerfile as the Dockerfile path.

storefront-web deployment’s Build log shows 📦 Building and pushing docker image using apps/web/Dockerfile... instead of Ingot’s build plan, and its Dockerfile tab shows your Dockerfile. When the deployments are Deployed, each domain opens its own starter page again. INGOT_APP has no effect on Dockerfile builds, so you can keep it. See Dockerfile for build arguments and private base images.
Deploy changes
With Auto-deploy on, every push to the branch starts a deployment of both applications; otherwise, click Deploy now on each application for each change. Each deployment builds the image, then replaces the web process’s instances. See Deployments.Deploy only the apps that changed
To deploy an application only when its own files change, set its deploy paths:Open the deploy paths
Allow the app's files
storefront-web, add apps/web/**, packages/** and pnpm-lock.yaml. Click Update deploy paths.Connect the apps to each other
Applications in the same location can reach each other over the private network without going through the internet. On one application’s Networking tab, under Connected services, add an internal connection to the other with Add environment variables to the application turned on: Sevalla addsAPP_HOST, the other application’s internal hostname. See Networking.
Add a custom domain
To serve an application from your own domain, such asshop.example.com, add the domain on the application’s Domains tab and create the DNS records that Sevalla shows. Custom domains need a web process larger than H1, so scale the web process first. See Custom domains.
Deploy with the API
The Sevalla API makes the same deployment as Deploy to Sevalla, for both apps, without the dashboard. Run the steps in order in one shell: each step is one API call, and saves the IDs that later steps need in shell variables. Each step also names the CLI command and the MCP server tool that do the same. A variable that holdsnull means the call failed: run the curl command without its jq part to read the error.
You need:
- An API key with the Company developer role, or the
APP:CREATE,APP:READandAPP:UPDATEpermissions, in theSEVALLA_API_KEYenvironment variable. The CLI reads the key fromSEVALLA_API_TOKEN. - The repository on GitHub, GitLab or Bitbucket, and a Git account that can read it connected to your Sevalla user, which only the dashboard can do. See Git providers. Creating the application checks the access: if Sevalla can’t read the repository, the call returns
400withCannot access repository https://github.com/acme/storefront: Repository not found or you don't have access., followed by where to grant the access. curlandjq.
Find the location
listClustersV3) returns every location with its id, its name in display_name and its region ID in location. Pick one by its region ID, here eu-frankfurt-1 for Frankfurt, Germany. Locations lists them all.sevalla resources clusters. MCP: get_platform_options.Find the H1 size
listProcessResourceTypesV3) returns the ID of each size:sevalla resources process-types. MCP: get_platform_options.Choose the app
APP. Run them for web first, then again from this step for docs:storefront-web then storefront-docs.Create the application
createApplicationV3) creates storefront-$APP from the repository, with the Ingot build strategy and a web process, and doesn’t deploy it. Both applications use the same repository and branch.sevalla apps create --name "storefront-$APP" --cluster "$CLUSTER_ID" --source privateGit --git-type github --repository https://github.com/acme/storefront --branch main. MCP: create_application with deploy set to false.Find the web process
listProcessesV3) returns the web process that Create application added:sevalla apps processes list --app-id "$APP_ID". MCP: get_application, in processes.Set the web process size
updateProcessV3) sets the size, which applies at the first deployment: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.Add the INGOT_APP variable
createEnvironmentVariableV3) adds INGOT_APP. Ingot reads it during the build to pick the app, so it needs to be available during the build only.sevalla apps env-vars create "$APP_ID" --key INGOT_APP --value "apps/$APP", which makes the variable available during both the build and runtime. MCP: set_environment_variables with runtime set to false and buildtime set to true.Turn on auto-deploy
updateApplicationV3) with auto_deploy deploys every push to the branch:sevalla apps update "$APP_ID" --data '{"auto_deploy":true}'. MCP: sevalla_api_update with updateApplicationV3, the application ID as path_params.id and the same body.Choose the Dockerfile build strategy
updateApplicationV3) sets the build strategy:sevalla apps update "$APP_ID" --data "{\"build_type\":\"dockerfile\",\"dockerfile_path\":\"apps/$APP/Dockerfile\",\"docker_context\":\".\"}". MCP: sevalla_api_update with updateApplicationV3 and the same body.Deploy
triggerDeploymentV3) deploys the latest commit of the branch:sevalla apps deployments trigger "$APP_ID". MCP: trigger_deployment.Wait for the deployment
getDeploymentV3) returns the deployment’s status: waiting, inProgress, then success, failed, cancelled or skipped. Poll it until the deployment ends:success means the application is deployed. CLI: sevalla apps deployments get "$DEPLOYMENT_ID" --app-id "$APP_ID". MCP: get_deployment.Read the build log
getApplicationDeploymentLogsV3) returns the build log, one line per entry. With Ingot, the App row of the build plan shows the app that INGOT_APP picked, and the Start row the start command:📦 Building and pushing docker image using apps/web/Dockerfile... (or apps/docs/Dockerfile) in place of the build plan. When a deployment fails, the end of this log says why. CLI: sevalla apps deployments logs "$DEPLOYMENT_ID" --app-id "$APP_ID". MCP: get_deployment_logs.Check the application
listApplicationDomainsV3) returns the application’s domains. Request the application on its domain. Its page names the file to edit, which differs between the two apps:docs, the output is <code>apps/docs/app/page.tsx</code>. After the web pass, run the steps again from the step “Choose the app”, with APP=docs.CLI: sevalla apps domains list --app-id "$APP_ID". MCP: get_application, in domains.