Skip to main content

Deploy Node.js Apps

Node.js is a highly-efficient JavaScript runtime environment that executes JavaScript code as a server. Node.js is served using Phusion Passenger inside NGINX.

Popular Node.js recipes include Express, Next.js, Nuxt.js, SvelteKit. Please read our Runner's Guide first if you haven't.

info

This guide only covers running a Node.js server, not about statically rendered pages like from Create React App, Webpack, Parcel or Gatsby. If your application is intented to be as a static site, you should read our Static Site Guide instead.

Recipes

Init in Development Mode

source: clear
features:
- node lts
nginx:
root: public_html/public
passenger:
enabled: on
app_env: development
app_start_command: env PORT=$PORT npx nodemon ./bin/www
commands:
- npx express-generator --view=pug --css=less --git .
- npm i
- npm add -D nodemon

A simple express website with nodemon for development.

Switch to Production Mode

nginx:
root: public_html/public
passenger:
enabled: on
app_start_command: env PORT=$PORT npm start

This switches Express to production mode.


Let's extract those recipes meaning individually.

Node environment

The default Node version is 16.x, which is the default provided from the OS.

To change Node version used to the latest (LTS) one, put this in runner:

features:
- node lts

It will install node in userland and all binaries will use it instead of the default one.

You can also install other or specific version of Node.js e.g. node latest, node beta, node 16.3.2. This action will install Node.js in userland with the help of webi script and enabling Corepack so package managers (npm & yarn & pnpm) can be used alongside userland node.

NGINX Setup

Binding Node.js through NGINX is done by Passenger. To make the binding work, you need to make sure that your app can open port number using given environment variable (.e.g. PORT), and you point the root of your public file to a public directory.

root: public_html/public
nginx:
passenger:
enabled: on
app_start_command: env PORT=$PORT npm start

If your setup is complex (e.g. using multiple websites in a domain) you can tell which exactly the app_root directory your app is serving from. Just make sure your root directory is outside of your app directory.

root: public_html/client/dist
nginx:
locations:
- match: /api
passenger:
enabled: on
app_start_command: env PORT=$PORT npm start
app_root: public_html/server

Some frameworks like Next.js likes to serve a hidden static directory e.g. /_next. We can reduce server load by creating an extra /_next location so those files is directly handled (and properly cached) by NGINX.

nginx:
locations:
# optimizing static cache
- match: /_next/
alias: public_html/.next/
info

There's so much more to cover about NGINX configuration! read more at NGINX page.

Package Install

Package installs can be done just like usual npm/yarn/pnpm install command. For a little advice, using these extra options is recommended in production:

npm ci
yarn install --immutable

Clear packager cache

In meantime if your development has stable enough, you may want to clear the packager cache to save space.

npm cache clean --force
yarn cache clean --force
pnpm cache clean --force

Node.js Error Logs

When your Node.js crashed during startup, a helpful error will be displayed in the browser. This aids you to diagnose if some configuration is wrong.

(TODO) Unfortunately, we haven't found a way to capture Node.js error logs yet. You can (should?) do error logging to files using builtin Node.js Console API or use Winston.