Supported apps

Vinssi inspects the package.json in your project's root directory and picks one of three app types. Detection happens on every deployment, so changing your dependencies changes how the app is built and started.

Detection rules

Rules are checked in this order. The first match wins.

App typeDetected when
Next.jsnext is in dependencies or devDependencies and a next.config.js, .ts or .mjs file exists
Vitevite is in dependencies or devDependencies and a vite.config.js, .ts or .mjs file exists
Node backendany of express, fastify, hono or @nestjs/core is in dependencies or devDependencies

If none match, the deployment fails at the build step with a message listing the supported stacks. A Next.js or Vite project without its config file is not detected; add the file even if it is empty.

Package manager

Vinssi picks the package manager from, in order: the packageManager field in package.json, then the lockfile present (bun.lock or bun.lockb, pnpm-lock.yaml, yarn.lock, package-lock.json). With no lockfile it falls back to npm.

Default commands

You can override any of these in Build settings. The defaults are:

Install

Package managerCommand
npmnpm ci when package-lock.json exists, otherwise npm install
pnpmpnpm install --frozen-lockfile
yarnyarn install --frozen-lockfile
bunbun install --frozen-lockfile

Frozen installs fail when the lockfile is out of date. Commit an updated lockfile rather than overriding the install command.

Build

If your package.json has a build script, Vinssi runs it with the detected package manager. Otherwise it runs the framework default: next build, vite build, or tsc for a Node backend.

Start

App typeStart command
Next.jsnext start -p 3000, always, even if you have a start script
Node backendyour start script via npm run start, or node dist/index.js if there is none
Vitethe built dist folder is served as a single-page app on port 3000

The start script always runs through npm because the runtime image contains only Node. A start script that calls bun or pnpm will fail; use plain node.

The two rules every app must follow

Listen on port 3000

Vinssi sets PORT=3000 in the running container and sends all traffic there. A server that hardcodes another port will build fine and then never become reachable. Read process.env.PORT or listen on 3000 explicitly.

Setting your own PORT variable has no effect; the platform value wins.

Run on Node 22

Builds and the runtime both use Node 22. The Node version you declare in engines.node, .nvmrc or the build settings is recorded with the deployment but does not change the Node that runs. Code that requires a newer Node or an older one will fail in ways that look like ordinary runtime errors.

Monorepos

Set Root directory in build settings to the folder that contains the app's package.json, for example apps/web. Install, build and start all run inside that folder, and only that folder is packaged into the deployment.

That has one consequence: the app must be installable on its own from that folder. Workspace links to sibling packages, such as "@acme/ui": "workspace:*", will not resolve during install. Publish shared packages to a registry or inline them until workspace-aware builds land.

Static output

A Vite build is served from a container rather than a CDN, with single-page-app fallback so client-side routes resolve. If you only have static HTML to publish and no build step, Vinssi Pages is faster and cheaper.

What runs where

  • Build runs in an isolated, short-lived container with 2 CPUs and 4 GB of memory available, a 25 minute hard limit, and network access to the public internet for fetching dependencies.
  • Runtime runs a single instance with the resources of the project's plan, no persistent disk, and outbound access to the public internet.