Node.js, Next.js and Nuxt applications.

How Node.js projects are detected and started: Node version, package manager, Next.js and Nuxt (server or static), SvelteKit, Astro, Express and friends, the PORT variable and Prisma migrations.

3 MIN READ

A repository with a package.json is a Node.js project. We pick the framework, the package manager and the Node version, build it and start it — you only push.

Node version and package manager

  • Node.js — taken from engines.node in package.json or from .nvmrc: the newest of 24, 22, 20, 18 or 16 that fits. Without either, Node.js 22.
  • Package manager — by the lock file: pnpm-lock.yaml → pnpm, yarn.lock → yarn, otherwise npm (npm ci when there is a package-lock.json).
  • Build — the build script, when package.json has one.

Frameworks

FrameworkHow it runs
Next.jsas a server (next start) — server rendering, API routes and middleware work as usual
Next.js with output: 'export'as a static site from out
Nuxtas a server (.output/server/index.mjs)
Nuxt with ssr: false and a generate scriptas a static site from .output/public
SvelteKitas a server with adapter-node; with adapter-static, as a static site
Astroas a static site; with @astrojs/node, as a server
NestJSdist/main.js after the build
Express, Fastify, Koa, Hono and other serversthe file in scripts.start (node server.js), else main, else server.js, app.js, index.js or main.js

Front-end-only projects — Vite, Create React App, Angular, Vue CLI and others — are served as static sites; see Static sites.

Listen on PORT

A server must listen on the port in the PORT variable, on all interfaces:

const port = process.env.PORT || 3000
app.listen(port, '0.0.0.0')

Next.js and Nuxt do this by themselves. If your application listens on a fixed port, it will not receive visitors — the deploy then stops at the start check and the previous version keeps running.

Build-time values

Variables from the Variables tab reach the running application, not the build. Values that the build writes into the client code — such as NEXT_PUBLIC_… in Next.js or VITE_… — are read from your repository, for example from a committed .env.production file. Server-side code reads the Variables tab as usual.

Your own start command

If the detected start isn't what you want, set Settings → Build settings → Start command, e.g. node dist/server.js or npm run start:prod. Install and build commands can be changed there too.

Databases and Prisma

Add PostgreSQL or MySQL to the project and read DATABASE_URL — it is set for you. With Prisma in your dependencies, the recommended release command is npx prisma migrate deploy: it runs on every deploy before the new version receives visitors. See Deploys.

For queues with BullMQ, add Valkey and use REDIS_URL. Run the queue worker as a background worker — on the Processes tab, or as a separate service with its own resources. See Services of a project.

Files that outlive a deploy

Each deploy starts from a fresh copy of your repository. Files your application writes to /data are kept between deploys and backed up daily; anything written elsewhere is gone after the next deploy. For uploads and media, object storage is usually the better choice.

Monorepos

For a repository with several projects, set the Project folder when you create the application (e.g. apps/web). Another folder of the same repository can run as a separate web service — see Services of a project.

Still stuck?

Chat with a real engineer — median response under 3 minutes, any time of day.

Contact support