Public testing phase We’re in a public testing phase — feel free to look around, but we’re not taking orders yet. Ordering opens 1 October 2026. See plans →

Node.js apps: Next.js, Nuxt, SvelteKit and Express

A JavaScript app site runs your own Node.js server: Next.js, Nuxt, SvelteKit, Express, or anything else that listens on a port. We build it from Git, start it as a supervised service and put nginx in front. This article covers what your app must do, and what we do for it.

How it runs

  • Node.js 22 or 24 (long-term support builds), switchable on the site page under Advanced → Node.js.
  • Your app is started by the system service manager as its own user, restarted if it crashes, and given a memory limit that fits your plan.
  • nginx listens on the outside. Anything in your project's public/ folder is served straight from disk; every other request — pages, API routes, WebSockets — is proxied to your app.
  • TLS, your domain, backups and monitoring work exactly as for every other site.

Deploying

Connect a repository under Publish → Deploy from Git (see Deploy from Git). On every deploy we:

  1. run your install command (npm ci unless you set one) and your build command (npm run build when your package.json has a build script) in a separate build container;
  2. copy the built project — without node_modules — into your site;
  3. install production dependencies there with your site's own Node version (npm ci --omit=dev);
  4. restart your app and wait for it to answer on its port. A deploy only counts as done once it does — a broken start command or a missing dependency fails the deploy and keeps the log for you to read.

The start command

By default we run npm start if your package.json defines it, otherwise node server.js or node index.js. When your framework needs something else, set it on the site page under Advanced → Node.js → Start command:

FrameworkBuild commandStart command
Next.jsnpm run buildnpm start (the default)
Nuxtnpm run buildnode .output/server/index.mjs
SvelteKit (adapter-node)npm run buildnode build
Express / plain Nodeusually nonenode server.js (the default)

The command runs from your project root, with your site's Node version first on the PATH.

The PORT environment variable

Your app must listen on the port in process.env.PORT (we set it to 3000) and on 127.0.0.1 or all interfaces — nginx connects to it locally. Next.js, Nuxt and SvelteKit's Node adapter read PORT by themselves; in Express it is one line:

app.listen(process.env.PORT || 3000);

We also set NODE_ENV=production and a sensible V8 heap size for your plan. Add your own variables — API keys, database URLs — under Advanced → Environment variables; they are written to the service's environment and your app is restarted, so it sees them on the next request. If your app talks to the managed database, the DB_* variables are already there: read them with process.env.DB_HOST and friends.

Behind a proxy

Requests reach your app over plain HTTP from nginx, with the real details in headers: X-Forwarded-Proto (https), X-Forwarded-For, X-Real-IP and X-Forwarded-Host. Tell your framework to trust them so it builds https:// links and sees the visitor's address — in Express, app.set('trust proxy', 1); Next.js and Nuxt handle it out of the box.

Static assets and WebSockets

Put files that never change — images, fonts, downloads — in public/: nginx serves them without waking your app and adds a week of browser caching. Framework asset routes (/_next/static, /_nuxt) are served by your app as usual. WebSocket upgrades are passed through, so Socket.IO, live reload or a chat feature need no extra setup.

When something breaks

The Logs tab shows your app's own output (everything it prints to the console) newest first — a failed start is right at the top. Common causes: the app listens on a fixed port instead of PORT, a dependency is in devDependencies but needed at runtime, or the start command points at a file the build did not produce. Fix, push, deploy again.