Guides

Node.js — Next.js, Express and more

How Meghly recognises, builds and runs a Next.js, Express or any other Node.js app — with workers and migrations.

What runs

  • Node.js 24 and 22.
  • Package managers: npm, yarn or pnpm — told apart by the lock file (package-lock.json, yarn.lock, pnpm-lock.yaml) or packageManager in package.json.
  • Frameworks: Next.js, Express, and any server with a start script (Fastify, NestJS, Koa, Hono …).
  • A front-end only (React, Vue, Vite, Angular …) runs as a static site — see Static sites.

Node version

Meghly takes the version in this order: .nvmrc → .node-version → engines.node in package.json. With none, the default version. To be sure, set one:

package.json
{
  "engines": { "node": "22" }
}

What Meghly runs

When it findsWeb process
a next dependencynpm run build, then next start -p 8080
a start scriptnpm start (or the yarn/pnpm equivalent)
no start, but main or server.js, index.js, app.js, main.jsnode <that file>

A build script in package.json runs during the build. For your own server, listen on PORT and on 0.0.0.0:

server.js (Express)
const express = require("express");
const app = express();

app.get("/", (req, res) => res.send("Hello from Meghly"));

app.listen(process.env.PORT || 8080, "0.0.0.0");

Workers

A script called worker, queue or jobs in package.json runs as a separate worker process — a BullMQ or queue consumer, for example:

package.json
{
  "scripts": {
    "start": "node server.js",
    "worker": "node jobs/worker.js"
  }
}

For more control, write a Procfile.

Migrations

They run in the release before each deploy (with the migration login):

  • a migrate, db:migrate or prisma:migrate script, if there is one;
  • with Prisma, prisma migrate deploy;
  • for anything else (Knex, TypeORM, Drizzle, Sequelize), set your own command in the app's General → Release command, such as npx knex migrate:latest.

Databases

A linked database brings DATABASE_URL — Prisma, Drizzle and Knex all read it directly:

prisma/schema.prisma
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

A linked Redis brings REDIS_URL, RabbitMQ RABBITMQ_URL. See Databases.

Next.js tips

  • Variables starting with NEXT_PUBLIC_ are baked into the code at build time. Changing one needs a new deploy, not just a restart.
  • The next/image cache and anything else written at run time goes to /tmp or a volume; writing to .next/cache while running is not reliable.
  • WebSockets (socket.io) work on the app's HTTPS address with no extra setting.

When something goes wrong

What you seeCause and fix
Detect: "No start script, main file or server.js"Add a start script to package.json.
The deploy stops at the health checkCheck the app listens on process.env.PORT and 0.0.0.0.
EROFS: read-only file systemYou are writing into the app's folder — use /tmp or a volume.
The wrong Node versionSet .nvmrc or engines.node.

Stuck?

Talk to our engineers directly — by phone or email.

01782618248shohag.meghly@gmail.com