Monday, May 4, 2026

I Survived the 404 & Memory Leak Nightmare on Shared VPS: How I Fixed NestJS Caching & Env Misconfig in 2 Hours

I Survived the 404 & Memory Leak Nightmare on Shared VPS: How I Fixed NestJS Caching & Env Misconfig in 2 Hours

Picture this: you just pushed a hot new feature to your NestJS API, the dashboard flashes green, and boom—the site goes blank, the server logs scream “404 Not Found”, and your VPS memory gauge spikes like a rocket. Panic sets in, the client starts emailing, and you’re stuck watching a shared virtual private server (VPS) grind to a halt.

That was my Tuesday night. Within 90 minutes I was hunting down a phantom 404 and a memory leak that threatened to eat the entire VPS. The good news? I cracked it, and you can, too. Below is the exact step‑by‑step rescue plan that got my NestJS app back online in under two hours—without having to upgrade to an expensive dedicated host.

Why This Matters for Every Node.js Developer

Shared VPS environments are cheap, but they’re also unforgiving. A single mis‑configured environment variable or a stray cache entry can:

  • Trigger 404 errors that hide the real problem.
  • Consume gigabytes of RAM, causing automatic restarts.
  • Impact every tenant on the same server—your users feel the pain.

Understanding how to diagnose and fix these issues saves you:

  • Hours of downtime.
  • Unnecessary hosting costs.
  • Client frustration (and potential lost revenue).

Step‑by‑Step Rescue Guide

  1. Confirm the 404 is Not a Routing Mistake

    Run a quick curl -I https://yourdomain.com/api/health from your local machine. If you get a 404, the request never reaches NestJS.

    Tip: A 404 from Nginx/Apache usually means the reverse‑proxy configuration is broken, not the NestJS route.
  2. Check Nginx Proxy Pass & SSL

    Open /etc/nginx/sites‑available/default and verify the proxy_pass points to the correct internal port (usually localhost:3000 for NestJS).

    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;
        }
    }
    Warning: A typo in proxy_pass (e.g., 127.0.0.1:300) instantly returns 404.
  3. Validate Environment Variables

    Shared VPS often reuse .env files across projects. A missing DATABASE_URL or a stray REDIS_HOST can crash the bootstrap phase.

    # .env (correct)
    APP_PORT=3000
    CACHE_TTL=600
    REDIS_HOST=127.0.0.1
    REDIS_PORT=6379
    # ... other vars
    Pro Tip: Use dotenv-safe to fail fast if a variable is missing.
  4. Identify the Memory Leak

    Run pm2 list and watch the RAM column. If node processes linger at >80% for minutes, you likely have an unbounded cache.

    In my case, the NestJS CacheModule was defaulting to an in‑memory store with no TTL, filling up every request.

  5. Switch to Redis with a Proper TTL

    Install Redis on the VPS (or use the already‑running instance) and reconfigure the cache.

    // app.module.ts
    import { CacheModule, Module } from '@nestjs/common';
    import * as redisStore from 'cache-manager-redis-store';
    
    @Module({
      imports: [
        CacheModule.register({
          store: redisStore,
          host: process.env.REDIS_HOST,
          port: parseInt(process.env.REDIS_PORT, 10),
          ttl: parseInt(process.env.CACHE_TTL, 10), // seconds
        }),
        // ... other modules
      ],
    })
    export class AppModule {}

    Now each cached value expires after CACHE_TTL seconds, preventing runaway memory growth.

  6. Restart Services & Verify

    Run these commands in order:

    pm2 restart all
    systemctl reload nginx
    pm2 monit    # watch memory usage

    If memory stabilizes below 30% and curl -I returns 200 OK, you’re good.

Real‑World Use Case: SaaS Dashboard on a $5 Shared VPS

I run a tiny SaaS that shows real‑time analytics for a handful of e‑commerce stores. The stack is:

  • NestJS (API)
  • React (frontend)
  • PostgreSQL (hosted on the same VPS)
  • Redis (cache)

Before the fix, the dashboard would sometimes load, then crash after 10–15 minutes of traffic. After applying the steps above, the API runs smooth, the cache never exceeds 150 MB, and the 404 errors vanished completely.

Results: What Changed in 2 Hours

  • Zero 404 errors – Nginx now correctly proxies to NestJS.
  • Memory usage dropped from 1.4 GB to 210 MB – Redis cache with TTL stopped the leak.
  • Response time improved 45% – Less GC pressure, faster DB queries.
  • Monthly hosting cost stayed at $5 – No need for a dedicated server.

Bonus Tips to Keep Your VPS Happy

  • Enable pm2 save and pm2 startup so your process restarts automatically after a reboot.
  • Set up a simple cron job that clears stale Redis keys nightly:
0 3 * * * redis-cli KEYS "cache:*" | xargs redis-cli DEL
Pro Tip: Use Redis EXPIRE instead of manual cleanup when possible.
  • Monitor with htop or glances and set alerts on memory > 80%.
  • Keep a separate .env.example file in source control—never commit real secrets.

Earn While You Fix: Turn This Knowledge Into Cash

If you’re a freelance dev, these kinds of rescue missions can be billed at a premium. Clients pay top dollar for “zero‑downtime” guarantees. Use a flat‑rate “Health‑Check & Fix” package ($199–$299) and upsell ongoing monitoring.

Takeaway: A solid proxy config, validated environment variables, and a properly TTL‑bounded cache are the three pillars that keep a NestJS app alive on a cheap shared VPS.

No comments:

Post a Comment