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 type | Detected when |
|---|---|
| Next.js | next is in dependencies or devDependencies and a next.config.js, .ts or .mjs file exists |
| Vite | vite is in dependencies or devDependencies and a vite.config.js, .ts or .mjs file exists |
| Node backend | any 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 manager | Command |
|---|---|
| npm | npm ci when package-lock.json exists, otherwise npm install |
| pnpm | pnpm install --frozen-lockfile |
| yarn | yarn install --frozen-lockfile |
| bun | bun 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 type | Start command |
|---|---|
| Next.js | next start -p 3000, always, even if you have a start script |
| Node backend | your start script via npm run start, or node dist/index.js if there is none |
| Vite | the 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.