Node.js & Express
This works for Express, Fastify, Koa, Hono, NestJS and any other Node.js server.
A minimal app
js
// server.js
const express = require('express')
const app = express()
const port = process.env.PORT || 8080
app.get('/', (req, res) => res.send('Hello from Express'))
app.listen(port, '0.0.0.0', () => console.log(`listening on ${port}`))json
{
"scripts": { "start": "node server.js" },
"dependencies": { "express": "^5.1.0" }
}What Zenavo detects
zbpack builds it as Node.js:
| Step | Command |
|---|---|
| Install | npm install, pnpm install, yarn install or bun install, after the lockfile in the repository (yarn without one) |
| Build | the build script, when there is one (TypeScript, bundlers) |
| Start | the start script; without one, node index.js |
The Node.js version comes from engines.node in package.json (22 when it is not set):
json
"engines": { "node": "22" }To use other commands, set ZBPACK_BUILD_COMMAND or ZBPACK_START_COMMAND under Settings → Build → zbpack options (Build Plan).
Deploy
- Add Service → GitHub, pick the repository and branch.
- Add variables, click Next, check the preview shows nodejs, and click Deploy.
- Generate a domain on the Networking tab.
Connect a database
Add a database to the project and reference its variables from the app's service:
sh
DATABASE_URL=${POSTGRESQL_CONNECTION_STRING}
REDIS_URL=${REDIS_CONNECTION_STRING}Run migrations when the app starts, not in the build: builds cannot reach the project's databases. A predeploy script in package.json runs before start every time the service starts:
json
"scripts": {
"predeploy": "node migrate.js",
"start": "node server.js"
}TypeScript
Compile in the build script and start the output:
json
"scripts": {
"build": "tsc",
"start": "node dist/server.js"
}Keep typescript in dependencies or devDependencies; both are installed for the build.
Common problems
| Symptom | Cause |
|---|---|
| Deploy never becomes healthy | The server listens on localhost or a fixed port. Listen on 0.0.0.0 and process.env.PORT. |
Cannot find module at start | The start script points at a file the build did not produce, or a dependency is missing from package.json. |
| The wrong package manager is used | The lockfile is missing or there are two of them. Keep exactly one. |
| Uploaded files disappear after a deploy | The container's disk is replaced on every deploy. Use a volume or object storage. |