Tuesday, May 5, 2026

Fixing the “NestJS Kills Itself on Unexpected Restart” Crash When Deploying to a Shared VPS – Why Chaotic PM2 Logs Hide Your Real Error and How to Uncover It in Minutes

Fixing the “NestJS Kills Itself on Unexpected Restart” Crash When Deploying to a Shared VPS – Why Chaotic PM2 Logs Hide Your Real Error and How to Uncover It in Minutes

Ever watched your NestJS app crash right after a VPS reboot, only to see a wall of noisy PM2 logs that make zero sense? You’re not alone. This article shows you exactly why those logs look like gibberish, how to expose the real stack‑trace, and a quick 5‑step fix that gets your API back up without endless guessing.

Why This Matters

If you’re running a SaaS, a micro‑service, or even a hobby project on a cheap shared VPS, uptime equals credibility (and cash). A sudden “NestJS kills itself on unexpected restart” error can shave minutes—or hours—off your SLA, frustrate users, and waste your billable hours debugging noise.

The Root Cause in One Sentence

PM2, the popular process manager, buffers crash logs and replaces the original process.exit() stack trace with its own wrapper. On a shared VPS where node_modules live on a network‑mounted drive, a missing .env or a permission glitch throws an ENOENT error that gets swallowed by PM2’s “restart” handler.

Step‑by‑Step Tutorial

  1. Confirm the Crash Is From NestJS, Not PM2

    Run the app manually without PM2:

    node dist/main.js

    If the process exits with Error: ENOENT: no such file or directory, open '.env', you’ve found the real culprit.

  2. Enable Full Stack Traces in NestJS

    Add the built‑in Logger and set process.env.NODE_ENV='development' temporarily.

    import { Logger } from '@nestjs/common';
    process.env.NODE_ENV = 'development';
    Logger.error('Boot error', err.stack);

    This forces NestJS to print the original error before PM2 hijacks the stream.

  3. Persist Critical Logs to a Separate File

    Tip: Using a dedicated log file prevents PM2’s rotating buffer from overwriting your stack trace.

    pm2 start dist/main.js \\
      --name my‑api \\
      --log-date-format='YYYY-MM-DD HH:mm:ss' \\
      --output ./logs/out.log \\
      --error ./logs/err.log
  4. Fix the .env / Permission Issue

    On a shared VPS, the home directory may be mounted with noexec. Ensure the file is readable by the user running PM2:

    chmod 640 .env
    chown youruser:yourgroup .env

    If you don’t have a .env file, create a minimal one:

    # .env
    PORT=3000
    DATABASE_URL=postgres://user:pass@localhost/db
  5. Restart with a Clean PM2 Dump

    Clear the old process list and reload:

    pm2 delete all
    pm2 save   # optional, clears dump file
    pm2 start ecosystem.config.js   # or the command from step 3

    Now PM2 will show a clean “online” status and your NestJS app stays up.

Full Example: ecosystem.config.js

module.exports = {
  apps: [
    {
      name: 'my-api',
      script: 'dist/main.js',
      instances: 1,
      autorestart: true,
      watch: false,
      max_memory_restart: '300M',
      env: {
        NODE_ENV: 'production',
        PORT: 3000,
      },
      error_file: './logs/err.log',
      out_file: './logs/out.log',
      log_date_format: 'YYYY-MM-DD HH:mm:ss',
    },
  ],
};

Warning: Never commit your .env file to Git. Use .env.example for defaults and keep the real file out of version control.

Real‑World Use Case

John, a freelance dev, deployed a NestJS auth service on a $5/month VPS. After the provider rebooted the server, his API started returning 502 errors. The PM2 dashboard was flooded with App[my-api] exited with code 1 messages, but no clue why.

By following steps 1‑3 he discovered the missing .env caused a ENOENT. After fixing permissions and pointing PM2 to dedicated logs, the service was back up in under 10 minutes—saving him a client call and a potential $150 penalty.

Results / Outcome

  • ⏱️ Reduce debugging time from hours to under 10 minutes.
  • ✅ Clear, searchable error logs in ./logs/err.log.
  • 🔧 Avoid false “PM2 restart loops” that waste CPU on cheap VPS plans.
  • 💰 Keep your SLA intact, protect revenue, and impress stakeholders.

Bonus Tips

  • Use pm2-runtime when containerizing with Docker to keep the same log handling.
  • Set PM2_LOG_DATE_FORMAT in your shell profile for consistent timestamps.
  • Enable node --trace-warnings in the PM2 ecosystem to catch deprecations early.
  • Consider Winston for structured JSON logs that integrate with services like Papertrail.

Monetization (Optional)

If you found this guide valuable, consider buying me a coffee or hiring me for a quick VPS health audit. I’ve helped dozens of startups keep their Node.js services alive on $5‑$10 servers.

Buy Me a Coffee Hire Me

No comments:

Post a Comment