MedhaCloud
Link copied to clipboard!
Managed IT Support

Node.js Hosting: Options, PM2 & Server Setup

Sreenivasa Reddy G
Sreenivasa Reddy G
Founder & CEO
Aug 3, 20269 min read
24
Node.js Hosting: Options, PM2 & Server Setup

Our Node.js server support team runs production Node.js apps on VPS, containers, and PaaS platforms. This page documents the options and the standard self-managed setup: hosting models with their cost and control trade-offs, Node version selection, server preparation, PM2 process management, an nginx reverse proxy, SSL, deployment, and the failures that account for most incidents.

Hosting options

OptionWhat it isTrade-off
VPS / dedicated (self-managed)A Linux server you administer: Node installed directly, a process manager keeps the app alive, nginx in front. The rest of this page describes this setup.Lowest cost per unit of compute and full control; you own OS patching, process management, TLS, and monitoring.
ContainersThe app packaged as a Docker image, run on a container host or an orchestrator (Kubernetes, ECS, Cloud Run). The image pins the Node version and dependencies.Reproducible builds and easy horizontal scaling; adds an image pipeline and orchestration layer to operate.
PaaS (Render, Railway, Heroku-class)Push code or connect a repo; the platform builds, runs, restarts, and terminates TLS. Render and Railway deploy from Git with per-service pricing; Heroku popularized the dyno model.No server administration at all; highest cost per unit of compute, and platform limits (build time, egress, background workers) apply.
Serverless functionsIndividual handlers (AWS Lambda, Cloudflare Workers, Vercel Functions) invoked per request, billed per execution.No idle cost and automatic scaling; cold starts, execution time limits, and no long-lived processes — WebSockets and persistent connections need different handling.

The self-managed VPS model is the default for apps with steady traffic: a $10–40/month server runs workloads that cost several times that on a PaaS. PaaS is the right answer when nobody on the team will maintain a server.

Which Node version to install

Install the Active LTS release unless a dependency requires otherwise. Per the Node.js release schedule, even-numbered major versions become LTS; odd-numbered versions are short-lived development releases and do not belong on production servers. As of August 2026, Node 24 (Krypton) is Active LTS, Node 22 (Jod) is in Maintenance LTS, and Node 26 is the Current release. Node 20 reached end of life in March 2026 — servers still on it should be upgraded.

Server setup

Two standard install paths. NodeSource provides distribution packages via its setup scripts (system-wide, one version); nvm installs per-user and switches versions easily. NodeSource on Ubuntu/Debian:

curl -fsSL https://deb.nodesource.com/setup_24.x -o nodesource_setup.sh
sudo bash nodesource_setup.sh
sudo apt-get install -y nodejs
node -v

Run the app as a dedicated non-root user. Node processes do not need root; a compromise of the app then stays inside that account:

sudo adduser --system --group nodeapp
sudo mkdir -p /var/www/myapp
sudo chown nodeapp:nodeapp /var/www/myapp

Deploy the code into that directory, run npm ci (not npm installci installs exactly what the lockfile specifies), and confirm the app starts by hand before adding a process manager.

PM2 process management

A Node process that crashes stays down until something restarts it. PM2 is the standard process manager: it restarts the app on crash, starts it on boot, and runs multiple instances behind Node's cluster module.

sudo npm install pm2@latest -g
pm2 start app.js --name myapp
pm2 save
pm2 startup

pm2 save writes the current process list to disk; pm2 startup prints a command that registers a systemd unit so the saved list resurrects after a reboot. Run both — a PM2 setup without them survives crashes but not reboots.

Cluster mode runs one worker per CPU core behind a shared port, which is how a single Node process uses a multi-core server:

pm2 start app.js --name myapp -i max

Day-to-day commands: pm2 list shows process status and restart counts, pm2 logs myapp tails stdout and stderr, pm2 restart myapp hard-restarts, and pm2 reload myapp restarts workers one at a time for zero-downtime deploys (cluster mode only). Crash restarts are automatic; a process that restarts too fast too often is put into an errored state rather than looping forever.

nginx reverse proxy

Do not expose Node directly on port 80/443. nginx in front terminates TLS, serves static files, and buffers slow clients. The canonical proxy block, including the headers WebSocket upgrades require:

server {
    listen 80;
    server_name example.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-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

The Upgrade and Connection headers are what make WebSockets work through the proxy; omitting them is the most common cause of Socket.IO connections silently falling back to polling. Bind the Node app to 127.0.0.1, not 0.0.0.0, so it is reachable only through nginx.

SSL

One command: Let's Encrypt via sudo certbot --nginx -d example.com obtains the certificate, rewrites the nginx config for HTTPS, and installs the renewal timer.

Environment and configuration

Configuration goes in environment variables, read from a .env file loaded at startup (Node 24 reads it natively with the --env-file flag; the dotenv package does the same on older versions). The .env file stays on the server and in .gitignore. Secrets committed to a repository are compromised the moment the repository is cloned anywhere; rotating them after the fact is the cleanup, not the fix. PM2 picks up environment changes on pm2 restart myapp --update-env.

Deployment patterns

  • Manual: git pull, npm ci, pm2 reload myapp. Adequate for one server and one deployer.
  • CI-driven: the same three steps executed by a pipeline (GitHub Actions over SSH is the common shape) on push to the main branch. Removes the "worked on my machine" deploys.
  • Zero-downtime: pm2 reload in cluster mode cycles workers one at a time, so requests are never dropped during a deploy. A hard pm2 restart drops in-flight requests; use it only when the reload path is unavailable.

Monitoring basics

pm2 monit shows live CPU and memory per process; pm2 list exposes the restart counter, which is the first thing to check when an app misbehaves — a climbing restart count means the app is crashing and PM2 is masking it. Add a health endpoint (/healthz returning 200 and a DB ping) and point an external uptime check at it; PM2 knows the process is up, not that the app is answering correctly.

Common failures

  • EADDRINUSE (port in use): a previous instance did not exit, or the app was started both by PM2 and by hand. Find it with lsof -i :3000; never run the app outside PM2 on the same port.
  • EMFILE (too many open files): the process hit the file-descriptor limit — either raise ulimit -n via systemd/PM2 limits, or fix the leak (unclosed sockets, streams, or watchers).
  • Memory-leak restart loops: heap grows until the process dies or --max-old-space-size is hit; PM2 restarts it and the cycle repeats. The restart counter climbs steadily. Capture a heap snapshot and find the retained objects; pm2 start app.js --max-memory-restart 500M is a containment measure, not a fix.
  • Crash loops on boot: the app throws during startup (missing env var, unreachable DB), PM2 retries, and the errored state appears in pm2 list. The cause is always in pm2 logs — read them before restarting again.

When to hand it over

Everything above is standard work, and it is also permanent work: OS patching, Node upgrades on the LTS schedule, certificate issues, restart-loop diagnosis at whatever hour they happen. Our application server support service covers Node.js hosting end to end — setup, PM2 and nginx configuration, deployment pipelines, and 24/7 incident response — alongside the other stacks described in application servers.

Node.js app down or restart-looping? Node.js support engineers handle crash diagnosis, PM2/nginx setup, and production hardening. Live chat is open 24/7.

Topics

nodejs-hostingpm2nginx
Sreenivasa Reddy G
Written by

Sreenivasa Reddy G

Founder & CEO15+ years

Sreenivasa Reddy is the Founder and CEO of Medha Cloud, recognized as "Startup of the Year 2024" by The CEO Magazine. With over 15 years of experience in cloud infrastructure and IT services, he leads the company's vision to deliver enterprise-grade cloud solutions to businesses worldwide.

Managed IT SupportCloud InfrastructureDigital Transformation
Follow on LinkedIn

Need Expert Help?

Our certified cloud and IT engineers are ready to tackle your toughest challenges — from migrations to managed services.