Deployment guide

Deploy a Node.js Application on a VPS

Deploy on a VPS Cloud →

Tutorial

Development

Deploy a Node.js Application on a VPS

Running a Node.js application in production takes more than a plain `node app.js`: you need a process manager that restarts it after a crash, environment variables kept out of the code, a reverse proxy for HTTPS, logs that do not fill the disk and automatic startup at boot. On a VPS you control every link in that chain, unlike a managed platform that bills you by usage. This guide starts from an Ubuntu server, either bare or prepared with the Node.js Stack template, and goes all the way to zero-downtime updates.

8 min read7 steps
Contents· Why Deploy Node.js on a VPS Rather Than on a Managed Platform?1/8
  1. 01Why Deploy Node.js on a VPS Rather Than on a Managed Platform?
  2. 02What You Gain with a VPS for Node.js
  3. 03Prerequisites
  4. 04Starting from the Node.js Stack Template
  5. 05Deploy a Node.js Application to Production
  6. 06Centralize the Configuration in ecosystem.config.js
  7. 07Logs: Reading Them, Then Rotating Them
  8. 08The Official Documentation

Why Deploy Node.js on a VPS Rather Than on a Managed Platform?

Serverless and PaaS platforms are convenient at first, but their cost rises with traffic and they impose constraints (timeouts, cold starts, limits on WebSocket connections). A VPS gives you a persistent Node.js process, long-lived connections (WebSocket, SSE) without artificial limits, dedicated resources and a fixed, predictable cost. You choose the Node version, the process manager, the reverse proxy and the update strategy, without depending on a provider's conventions.

Node.js's asynchronous, non-blocking model lets a single process hold a large number of simultaneous connections. That is what makes it a good fit for REST APIs, real-time features and microservices, as long as the process stays alive. This is exactly what most shared hosting refuses, by forbidding persistent processes or WebSockets. On a VPS, your npm dependencies, environment variables and PM2 configuration are not shared with any other account.

What You Gain with a VPS for Node.js

  • A persistent Node.js process, ideal for WebSocket, SSE and long-running tasks with no cold start
  • Automatic restart after a crash and when the server reboots, thanks to PM2
  • Cluster mode to use every CPU core of the VPS without changing your code
  • An Nginx reverse proxy in front of Node for HTTPS, compression and static files
  • Queue workers (BullMQ, Agenda) and scheduled jobs running next to the API on the same machine
  • Several applications on one VPS, each on its own internal port (3000, 3001…) and subdomain behind Nginx
  • Server-side rendering for frameworks such as Nuxt or Next.js, served by the same Node.js and PM2 foundation
  • A fixed, predictable cost, with no per-request billing and no scaling surprises
  • Full control over the Node.js version and system dependencies

Prerequisites

A standard Node.js application (Express, Nest or Fastify API) runs comfortably on a VPS with 1 to 2 vCPUs and 1 to 2 GB of RAM; plan for more RAM if you compile a heavy front end at build time or run workers. Aim for 2 vCPUs and 2 GB as soon as the API shares the machine with SSR rendering or a PostgreSQL database.

Pick an LTS line that is still maintained: Node.js 20 has received no fixes since 30 April 2026, Node.js 24 moved to maintenance on 20 October 2026, and Node.js 26 has been the active LTS since 28 October 2026. Install it through nvm or the NodeSource repository to pin it, along with PM2 as the process manager and Nginx as the reverse proxy. You need a domain pointing to the VPS IP and ports 80/443 open.

Starting from the Node.js Stack Template

The Node.js Stack template in the ServOrbit Marketplace prepares an Ubuntu 24.04 VPS with Node.js on the current LTS line of the NodeSource repository, npm, PM2 installed globally, Nginx and PostgreSQL. It saves you the first step below; the rest of the guide applies as is.

It is not an application with a login screen but a runtime environment: you get in over SSH (ssh root@<vps-ip>) to upload your code. On first access, Nginx serves its default welcome page on port 80, because no virtual host has been written for your domain yet, and Certbot is not installed: the Nginx and HTTPS steps are still yours to do. PostgreSQL listens only locally and authenticates the postgres system user with the peer method: sudo -u postgres psql opens the console without a password.

Deploy a Node.js Application to Production

  1. Prepare the VPS and Install Node.js

    On an Ubuntu 24.04 VPS, update the system and install an LTS version of Node through nvm (nvm install --lts) or the NodeSource repository. Check with node -v and npm -v. Create a non-root user dedicated to the application instead of running everything as root: that user will own the code, the .env file and the PM2 processes. With the Node.js Stack template, Node.js, npm and PM2 are already installed, so only the user creation remains.

  2. Fetch the Code and Build

    Clone your repository on the VPS, install the production dependencies (npm ci --omit=dev) and run the build if needed (npm run build).

    Put sensitive variables in an uncommitted .env file that only the application user can read (chmod 600 .env). No library is needed to read it: since Node.js 20.6, the --env-file option loads that file into process.env at startup.

  3. Prepare the PostgreSQL Database

    If the application needs a database, create a dedicated role and database instead of working with the superuser:

    sudo -u postgres createuser --pwprompt monapp
    sudo -u postgres createdb --owner=monapp monapp

    Then set DATABASE_URL=postgres://monapp:<password>@127.0.0.1:5432/monapp in the .env. Since PostgreSQL listens only locally, no database port has to be opened in the firewall.

  4. Start the Application with PM2

    Install PM2 globally (npm install -g pm2, already done on the template), then start the app from the project folder, asking Node to load the .env:

    pm2 start app.js --name monapp -i max --node-args="--env-file=.env"

    The -i max flag enables cluster mode across all cores. PM2 automatically restarts the process if it crashes.

  5. Enable Automatic Startup at Boot

    Generate the systemd script with pm2 startup, run as the application user, execute the command it prints with sudo, then freeze the current state with pm2 save. On the next VPS reboot, your application will be restarted automatically, with no intervention.

  6. Configure Nginx as a Reverse Proxy

    Install Nginx if it is not already there and create /etc/nginx/sites-available/monapp, a virtual host that proxies to Node's local port. The Upgrade and Connection headers are for WebSockets, and X-Forwarded-Proto tells the application that the client is on HTTPS:

    server {
        listen 80;
        server_name yourdomain.com;
    
        location / {
            proxy_pass http://127.0.0.1:3000;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }

    Enable the site, remove the default page, test the configuration and reload:

    ln -s /etc/nginx/sites-available/monapp /etc/nginx/sites-enabled/
    rm /etc/nginx/sites-enabled/default
    nginx -t && systemctl reload nginx
  7. Secure with HTTPS via Let's Encrypt

    Install Certbot and its Nginx plugin, then request the certificate:

    apt install certbot python3-certbot-nginx
    certbot --nginx -d yourdomain.com

    Certbot obtains the certificate, sets up the HTTP→HTTPS redirect and schedules automatic renewal. Your Node application is now served over HTTPS.

Centralize the Configuration in ecosystem.config.js

As soon as the VPS hosts more than one application, or an API comes with a worker, options passed on the command line become hard to keep track of. PM2 reads an ecosystem.config.js file that describes each process: script, folder, number of instances, memory limit and non-sensitive variables.

module.exports = {
  apps: [
    {
      name: 'api',
      cwd: '/home/monapp/app',
      script: 'app.js',
      instances: 'max',
      exec_mode: 'cluster',
      node_args: '--env-file=.env',
      max_memory_restart: '500M',
      env: { NODE_ENV: 'production', PORT: 3000 }
    },
    {
      name: 'worker',
      cwd: '/home/monapp/app',
      script: 'worker.js',
      node_args: '--env-file=.env'
    }
  ]
}

pm2 start ecosystem.config.js starts everything, pm2 reload ecosystem.config.js updates it, and the --only api option targets a single application. Here the API runs in cluster mode on every core and the worker as a single process. Version this file with the code; the secrets stay in the .env, outside the repository.

Logs: Reading Them, Then Rotating Them

PM2 captures the standard output and error output of every process in ~/.pm2/logs/, with one -out.log file and one -error.log file per application. pm2 logs monapp follows them live, and pm2 logs monapp --lines 100 shows the latest lines.

Without rotation, these files grow until the disk is full. The pm2-logrotate module takes care of it; by default it rotates a file past 10 MB and keeps 30 of them, thresholds that are easy to adjust:

pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 50M
pm2 set pm2-logrotate:retain 7

On the proxy side, a 502 error shows up in /var/log/nginx/error.log: it almost always means the Node.js process is stopped or listening on a different port from the one in proxy_pass.

For zero-downtime deployments, combine PM2 cluster mode with pm2 reload monapp rather than restart: PM2 restarts the workers one at a time, always keeping at least one instance available during the update. Then watch memory with pm2 monit and set an automatic restart limit (--max-memory-restart 500M) to guard against memory leaks in production.

The Official Documentation

For each tool's own options and version changes, refer to the official Node.js documentation, the Node.js LTS release schedule and the PM2 documentation. This guide covers putting an application into production on a VPS; the vendor documentation remains the reference for fine-tuning.

Put Your Node.js Application into Production on a Cloud VPS

Deploy a ServOrbit Cloud VPS with the Development template: Node.js, PM2, and Nginx preconfigured to move your application into production over HTTPS.