Skip to main content

Deploying Node.js on Vessl

Vessl detects Node.js automatically via package.json and builds your app with Nixpacks. This guide covers Express, Fastify, and other Node.js servers, as well as database connections, background workers, and TypeScript setups.

Prerequisites

  • A server added and provisioned in Vessl
  • A GitHub repository with your Node.js app
  • package.json at the repository root (or in your Root Directory for monorepos)

What Vessl auto-detects

When Vessl finds package.json it sets:

SettingAuto-filled value
Build engineNixpacks
Port3000
Post-deploy commandnpx prisma migrate deploy (if prisma/ folder detected) or npm run db:migrate (if drizzle/ folder detected)

Nixpacks reads your package.json scripts:

  • Install: npm ci (or yarn install / pnpm install depending on lockfile)
  • Build: runs npm run build if a build script exists
  • Start: runs npm run start

Step 1 — Make sure your app listens on PORT

Vessl injects a PORT environment variable. Your server must read it — hardcoding a port number will cause the health check to time out.

// Express example
const express = require('express');
const app = express();

const port = process.env.PORT || 3000;
app.listen(port, '0.0.0.0', () => {
console.log(`Server running on port ${port}`);
});
// Fastify / TypeScript example
const port = parseInt(process.env.PORT ?? '3000', 10);
await app.listen({ port, host: '0.0.0.0' });
Listen on 0.0.0.0, not localhost

By default, Node.js servers bind to 127.0.0.1. Inside a container, this means the health check from outside the container can't reach it. Always bind to 0.0.0.0.

Step 2 — Create the app

  1. In your project, click New Application.
  2. Select your Node.js repository and branch.
  3. Vessl detects Node.js — the port defaults to 3000.
  4. If you have a TypeScript build step, verify the build command is set to npm run build.
  5. Click Deploy.

Step 3 — TypeScript setup

Nixpacks compiles TypeScript projects automatically if it detects a tsconfig.json. Make sure your package.json has:

{
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
}
}

If you use ts-node or tsx directly in production (no compile step), your start command would be:

npx tsx src/index.ts

Set this in the Start command field in the wizard or app Settings.

Step 4 — Add a database

From the Add-ons tab, add PostgreSQL, MySQL, or Redis. Vessl injects connection variables automatically:

PostgreSQL / MySQL:

DATABASE_URL=postgresql://<user>:<pass>@<host>:5432/<db>
DB_HOST=<internal hostname>
DB_PORT=5432
DB_DATABASE=<db name>
DB_USERNAME=<username>
DB_PASSWORD=<password>

Redis:

REDIS_URL=redis://:<password>@<host>:6379
REDIS_HOST=<internal hostname>
REDIS_PORT=6379
REDIS_PASSWORD=<password>

Prisma

If your repo has a prisma/ directory, Vessl sets the post-deploy command to npx prisma migrate deploy automatically. Make sure DATABASE_URL is set (it is, via the add-on) and your prisma/schema.prisma uses the env("DATABASE_URL") datasource.

Drizzle

If your repo has a drizzle/ directory, Vessl sets npm run db:migrate. Add a db:migrate script to your package.json:

{
"scripts": {
"db:migrate": "drizzle-kit migrate"
}
}

Step 5 — Background workers

For queue processors, scheduled jobs, or WebSocket servers, add a daemon from the Daemons tab:

Use caseCommand
BullMQ workernode dist/workers/queue.js
cron loop/bin/sh -c "while true; do node scripts/cron.js; sleep 60; done"
WebSocket servernode dist/ws-server.js

Each daemon runs in its own container with the same environment variables as your web app.

Step 6 — Node version

Nixpacks picks the Node.js version from (in order):

  1. .node-version file
  2. .nvmrc file
  3. engines.node field in package.json
  4. Latest LTS if none of the above

To pin a specific version, add to package.json:

{
"engines": {
"node": "20.x"
}
}

Or create a .node-version file:

20

Common issues

Health check times out after a successful build The most common cause is the server not listening on 0.0.0.0. Check that your app.listen call uses 0.0.0.0 as the host and reads process.env.PORT.

npm run start exits immediately If your start script is something like node dist/index.js and the dist/ folder doesn't exist, your npm run build step didn't run or failed silently. Check the build logs in the deployment drawer.

TypeScript compilation errors in CI The Nixpacks build runs tsc with your tsconfig.json settings. If you use "strict": true, errors that TypeScript tolerates in development (any types, missing types) will fail the build. Fix the errors or add "noEmitOnError": false to tsconfig.json if you want to deploy despite type errors.

Out-of-memory during build Large TypeScript projects can run out of memory during tsc. Set:

NODE_OPTIONS=--max-old-space-size=4096

in the Variables tab before deploying.