Node.js
Deploy Next.js, Nuxt, Express or any Node.js app. We detect your framework and package manager, build it, and start it on port 8080.
Updated 17 Oct 20263 min read
A Node.js app deploys from its package.json. We choose Node.js 22 or 24, install your packages with the manager your lock file names, run your build script if there is one, and start the app. Your app must listen on the port in the PORT variable, which is always 8080.
Versions
| Version | Notes |
|---|---|
| Node.js 24 | What new apps get when the code doesn't ask |
| Node.js 22 | Still supported |
We read the version your code asks for from a version file like .nvmrc first, then from the Node.js version in the engines field of package.json, and pick the newest version we offer that meets it. >=20 builds with Node.js 24; ^22 or 22 builds with 22. If the code asks for one we don't offer, we build with Node.js 24 and tell you so. To pin one, choose it in the app's Build and run settings, or set "version": "22" in tanzahost.json.
What we detect
| Your code has | We use |
|---|---|
pnpm-lock.yaml | pnpm |
yarn.lock | Yarn |
package-lock.json | npm, with npm ci |
| No lock file | npm, with npm install |
Development packages are installed too, because builds need them (Vite, Tailwind CSS, TypeScript). A Bun lock file isn't used yet: the packages are installed with npm instead. NODE_ENV is production for the build and the app.
We recognise Next.js, Nuxt, Remix, SvelteKit, Astro, NestJS, Express, Fastify, Koa and Hono. Then we start the app this way, first match wins:
A
startscript inpackage.json:npm run start(or pnpm's or Yarn's).Next.js without one:
next start -p ${PORT}.Nuxt: its built server,
.output/server/index.mjs, with Node.js.The
mainfile ofpackage.json, or else the first ofserver.js,server.mjs,index.js,app.jsormain.js, with Node.js.
A project with a build script and no way to start, like a React or Vue app made with Vite, is served as a static site.
Listen on PORT
Visitors reach your app's web process on port 8080, given to it as PORT. Listen on every address, not only localhost:
const express = require("express");
const app = express();
app.get("/", (req, res) => res.send("Hello from duka!"));
app.listen(process.env.PORT || 8080, "0.0.0.0");A new version gets visitors only once it answers its health address ("/" unless you choose another) with a success, within 2 minutes. See Deploying.
Example: Asha's shop API
Asha adds a second app, api, to her project duka, from a GitHub repository with Express, a package-lock.json, and an .nvmrc file that says 22. Its package.json has:
{
"scripts": {
"build": "tsc --noEmit",
"start": "tsx src/server.ts"
},
"dependencies": {
"express": "^5.1.0",
"tsx": "^4.20.0"
}
}We find Express on Node.js 22, run npm ci --include=dev, then npm run build (her type check), then start it with npm run start. It's live on its own free address, like api-duka-k2m9.tanzahost.co. Her shop's database is attached to it too, so DATABASE_URL is already in its variables.
Build variables
Next.js and Vite put some variables into the built files (like NEXT_PUBLIC_… and VITE_…), so builds get your app's variables too. After changing one of those, deploy again to rebuild.
Change what we found
On the app's page, Build and run shows the language, version, folder, and the install, build and start commands we worked out. Change any of them; an empty field uses what we found. For an app in a folder of a larger repository (a monorepo), set Folder, like apps/api.
Workers
A queue consumer or any long-running script runs as a worker beside the web process, with its own command, like npm run worker. See Workers.