Guides
Application or static site
Choose by whether the project needs a server when a request arrives.How Sevalla builds an application
New applications built from Git use Ingot, Sevalla’s builder. Ingot readspackage.json and the lock file, writes a Dockerfile for the project, and Sevalla builds it:
The image runs with
NODE_ENV=production, as user 1000:1000, from /app. Environment variables marked Available during buildtime reach the install and build steps; see Environment variables during the build.
Before the build starts, the build log shows Ingot’s build plan: each decision and the file it came from. For the Express guide, it reads:
Build log
ingot.json file or set INGOT_* environment variables; see Configure the build. To build the image yourself, see Build with a Dockerfile.
Listen on the PORT environment variable
Sevalla sets thePORT environment variable in the web process to the process’s port, 8080 by default, and sends every request for the application’s domains to that port. Sevalla’s value replaces a PORT variable that you set yourself. Your server must:
- Listen on the port in
PORT. - Listen on all network interfaces (
0.0.0.0), not onlocalhostor127.0.0.1.
HOST=0.0.0.0 for Nitro, SvelteKit’s adapter-node, Astro’s @astrojs/node and react-router-serve. A server you write yourself must pass the port in code:
To use another port, update it on the application’s Networking tab: under Expose HTTP traffic, click Update port. Sevalla then sets
PORT to the new value. See Networking.
Set the start command
The web process runs the start command of the image the build produced. With Ingot, that’s thestart script in package.json, or the server of the framework Ingot detects; the build plan’s Start row shows which, and where it came from. To run something else without changing the repository, open the application’s Processes tab, open the web process’s menu, click Update process and fill in Custom start command. Ingot builds the next image with that command, so the change applies at the next deployment. See Set the start command for every way to set it.
Pin the Node.js version
Ingot builds on Node.js 24, its default line, unless the project asks for another version. Pin the version inpackage.json, so the build doesn’t change when a new Ingot release moves its default line:
INGOT_NODE_VERSION environment variable, versions.node in ingot.json, .node-version, .nvmrc, .tool-versions, mise’s configuration, then devEngines.runtime or engines.node in package.json. The build plan’s Node.js row shows the version and where it came from.
- A line, such as
24,24.xor^24, builds on that line’s newest release, and every new build picks up its security fixes. - A wider range, such as
>=20, builds on Ingot’s default line when the range allows it, otherwise on the newest maintained line it allows. - An exact release, such as
24.11.0, stays on that release and gets no security fixes, and the build log warns about it.
engines or .nvmrc. Sevalla installs the Node version chosen in the static site’s build settings, Latest LTS by default. See Node versions.
Build a static site
A static site build installs the dependencies with the package manager your lockfile names, runs the Build command in the Root directory, and publishes the Publish directory, which is relative to the root directory. See How a build runs. When you create a static site from a private repository, Sevalla readspackage.json and fills in the build command and publish directory for the frameworks it recognizes, including Astro (dist), Next.js (out) and Nuxt (.output/public, built with npm run generate). See Framework detection for the full list.
Build with a Dockerfile
Every guide that deploys an application also gives a Dockerfile for it, in its Deploy with a Dockerfile section, for example Express or Next.js. Switch the application to it on the Settings tab with Update build strategy. The guides’ Dockerfiles do what Ingot does for you:- Base images.
node:24-trixie-slim, the Node.js line thatengines.nodepins, withnode:24-trixieas a separate build stage when the build needs compilers. Bun and Deno guides useoven/bunanddenoland/deno. A line tag, where the image publishes one, picks up security fixes at every build. - Install from the lock file first. Copy
package.jsonand the lock file, runnpm ci, then copy the source, so a code change reuses the cached dependency layer. - Run as user
1000. The image’snodeuser, with the application’s files owned by root, so the server can’t change its own code. - Shut down cleanly. Run
nodewith an exec-formCMD, so it getsSIGTERMwhen an instance stops. Node.js ignoresSIGTERMwhen it runs as the container’s main process and has no handler for it, so servers you write yourself, such as Express, add one that closes the server. Ingot’s images runtinifor the same reason. - Keep secrets out.
.dockerignoreexcludes.envfiles. Build-time environment variables reach the build as build arguments; declare the ones the build needs, such asVITE_orNEXT_PUBLIC_variables, withARGin the build stage only. See Use build arguments.
/bin/sh -l -c. Start a long-running command with exec, such as exec node worker.js, so the worker itself gets SIGTERM. See Run background jobs with BullMQ.
Keep the application stateless
Each deployment starts new instances from a new image, so files written to an instance’s disk are gone after the next deployment, and instances don’t share memory when you run more than one. Keep state outside the process:- Sessions and cached data in Redis: see Use Redis from Node.js.
- Uploaded files in object storage: see Use object storage from Node.js.
- Data in a database, reached over an internal connection.