Tuesday, May 5, 2026

Why My NestJS App Crashes on a Shared VPS Every Hour—The Hidden Config Pitfall & Instant Fix

Why My NestJS App Crashes on a Shared VPS Every Hour—The Hidden Config Pitfall & Instant Fix

Hook: You finally got your NestJS micro‑service running on a cheap shared VPS, your dashboard flashes green, and then—boom!—the app disappears every 60 minutes. The console is a wall of “OOM killed” or “SIGTERM” messages, and you’re left wondering if you need a $100‑per‑month server. The truth? It’s not the code, it’s a silent config setting that most devs overlook.

Why This Matters

If you’re building SaaS tools, API gateways, or automation bots with NestJS, uptime is your reputation. One‑hour crashes mean missed webhooks, angry customers, and a skyrocketing churn rate. Plus, every reboot wastes CPU cycles that could be selling you more compute credits or, better yet, more billable hours.

The Hidden Config Pitfall

Shared VPS providers usually enforce a process‑level memory limit that resets when the Linux cgroup thinks a process is using too much RAM. By default, NestJS runs with node in development mode, which includes heavy source‑map support and an open watch mode. On a 512 MB VPS, this extra overhead pushes the process right over the limit, causing the OOM killer to terminate it after about an hour of traffic.

Warning: The crash won’t show up in your application logs because it’s the OS that ends the process. You’ll only see “npm start exited with code 143” in the system journal.

Instant Fix: Production‑Ready NestJS on a Shared VPS

Below is a step‑by‑step tutorial that trims the fat, configures Node’s memory usage, and adds a watchdog that restarts the app safely.

Step 1 – Install Node.js LTS & PM2

# Use nvm to get the latest LTS (e.g., 20.x)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.bashrc
nvm install --lts

# Global process manager
npm i -g pm2

Step 2 – Build NestJS for Production

Turn off source‑maps and the hot‑reload watcher.

// package.json scripts
{
  "scripts": {
    "build": "nest build --no-source-map",
    "start:prod": "node dist/main.js"
  }
}

Step 3 – Adjust Node Memory Limits

Node defaults to using up to ~1.5 GB of RAM, far beyond a shared VPS. Explicitly cap it.

# Create a .env file at the project root
NODE_OPTIONS="--max-old-space-size=150"
PORT=3000
# Optional: limit open files
ULIMIT_NOFILE=4096

Step 4 – Deploy with PM2 and Enable Auto‑Restart

# In your project folder
npm run build
pm2 start dist/main.js --name nest-app --node-args "$NODE_OPTIONS"
pm2 save                       # Persist across reboots
pm2 startup systemd -u $USER --hp $HOME

Step 5 – Add a Simple Health‑Check Endpoint

Helps you (and monitoring tools) verify the app is alive without generating heavy logs.

// src/app.controller.ts
import { Controller, Get } from '@nestjs/common';

@Controller()
export class AppController {
  @Get('health')
  healthCheck() {
    return { status: 'OK', timestamp: new Date().toISOString() };
  }
}

Step 6 – Set Up a Cron‑Based Watchdog (Optional)

If PM2 ever fails, a tiny cron job will restart it within a minute.

# Edit crontab -e
* * * * * /usr/local/bin/pm2 resurrect >/dev/null 2>&1

Tip: Run pm2 list after a few hours of traffic. You should see online with 0 restarts, confirming the OOM killer is no longer involved.

Real‑World Use Case: Auto‑Scaling a Webhook Processor

Imagine you built a NestJS service that ingests Stripe webhooks, enriches the payload, and stores it in a MySQL DB. On a shared VPS, you noticed that after the first 60 minutes of a sales spike, the webhook endpoint started returning 502 errors. Applying the steps above reduced memory usage from ~750 MB to ~180 MB, and the service stayed alive for days without a single restart.

Results / Outcome

  • Zero hourly crashes—uptime > 99.9%.
  • Memory footprint trimmed by ~75%.
  • CPU load dropped 30% because the hot‑reload watcher was disabled.
  • Customer support tickets related to “service unavailable” fell from 12/month to 0.

Bonus Tips for Long‑Term Stability

  • Enable Log Rotation: pm2 set pm2-logrotate:max_size 10M prevents log files from eating your disk.
  • Use a Small Reverse Proxy: Nginx in front of NestJS can buffer spikes and serve static assets.
  • Monitor Memory in Real Time: watch -n 5 "ps -o pid,pmem,pcpu,command | grep node".
  • Upgrade When Needed: If traffic regularly exceeds 300 requests/sec, consider moving to a managed container service.

Monetization Angle (Optional)

Turn the “instant fix” into a paid service: offer a one‑click Deploy script for NestJS on cheap VPS providers, bundle monitoring, and charge $9.99/month per instance. You’ll be solving a problem that dozens of freelancers hit every week.

By understanding the hidden memory limit on shared VPSes and tweaking NestJS for production, you not only stop the hourly crash but also free up resources to handle more traffic. The result? A smoother user experience, happier clients, and a leaner stack that saves you money—exactly what every dev‑entrepreneur wants.

No comments:

Post a Comment