Why My NestJS App Crashes on a Cheap VPS: A Deep Dive Into “Cannot Listen on 0.0.0.0:3000” vs “EADDRINUSE” Errors and How I Fixed It in Minutes
Imagine you just spun up a $5/month VPS, pushed your NestJS API, and watched the dreaded “Cannot listen on 0.0.0.0:3000” error explode on your console. You stare at the log, wonder if you’ve hit a wall, and fear your hard‑earned traffic will vanish. Sound familiar? You’re not alone.
Why This Matters
If you’re a solo dev, agency hacker, or side‑hustle entrepreneur, every minute your server is down is a lost opportunity—whether that’s sales, API calls, or user trust. Cheap VPS providers are great for prototypes, but they come with quirks: limited RAM, shared networking stacks, and unpredictable port bindings. Understanding the difference between a generic “cannot listen” error and the classic “EADDRINUSE” conflict can save you hours of debugging and keep your cash flow steady.
Step‑by‑Step Tutorial: Get Your NestJS App Running in Minutes
- Confirm the Port Is Free
Run
netstat -tulpn | grep 3000(Linux) orlsof -i :3000(macOS) to see if another process already owns the port. - Check Your NestJS
main.tsBind Addressimport { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000, '0.0.0.0'); // <- This line matters } bootstrap();If you see
'0.0.0.0', NestJS will bind to all interfaces. On some cheap VPSes the default network interface is not ready when the app starts, causing the “Cannot listen” error. - Delay the Listener Until the Network Is Up
// delay-listen.service.ts import { Injectable, OnModuleInit } from '@nestjs/common'; import { NestApplication } from '@nestjs/core'; @Injectable() export class DelayListenService implements OnModuleInit { constructor(private readonly app: NestApplication) {} async onModuleInit() { // Wait 2 seconds – tweak for your VPS await new Promise(res => setTimeout(res, 2000)); await this.app.listen(3000, '0.0.0.0'); console.log('NestJS listening on 0.0.0.0:3000'); } }Inject this service in
AppModuleand remove the originalapp.listencall. The tiny pause lets the VPS finish its network init. - Handle “EADDRINUSE” Gracefully
If the port is truly taken, you’ll see
EADDRINUSE. Instead of crashing, catch the error and switch to an alternate port.async function bootstrap() { const app = await NestFactory.create(AppModule); try { await app.listen(3000, '0.0.0.0'); } catch (err) { if (err.code === 'EADDRINUSE') { console.warn('Port 3000 in use – switching to 3001'); await app.listen(3001, '0.0.0.0'); } else { throw err; } } } bootstrap(); - Persist the Chosen Port Across Restarts
Store the final port in an environment variable or a small JSON file. That way your next deploy knows which port survived the conflict.
// .env PORT=3000 // main.ts import * as dotenv from 'dotenv'; dotenv.config(); const PORT = parseInt(process.env.PORT, 10) || 3000; await app.listen(PORT, '0.0.0.0');
Real‑World Use Case: SaaS API on a $5 VPS
Jane runs a micro‑SaaS that offers a JSON‑to‑PDF conversion endpoint. She uses a $5 DigitalOcean droplet because the traffic is low (< 500 requests/day). After a weekend reboot, her monitoring service flagged “Cannot listen on 0.0.0.0:3000”. Applying the steps above, she added a 2‑second startup delay and a fallback to port 3001. Within minutes the service was back online, and her users never saw a 5xx error.
Results / Outcome
- Uptime Boost: From 96% to 99.9% in one week.
- Zero‑Cost Fix: No need to upgrade the VPS.
- Developer Confidence: Knowing the exact error handling path saves 2–3 hours of frantic log‑scrolling per month.
Bonus Tips
Tip 1 – Use a Process Manager
PM2, forever, or systemd will automatically restart your Nest app if it crashes. Example PM2 ecosystem file:
// ecosystem.config.js
module.exports = {
apps: [{
name: 'nest-api',
script: 'dist/main.js',
instances: 1,
watch: false,
env: { NODE_ENV: 'production' },
restart_delay: 3000
}]
};
Tip 2 – Health‑Check Endpoint
Expose /health that returns {status:'ok'}. Your load balancer or uptime monitor can ping it every 30 seconds and trigger a restart if it fails.
// health.controller.ts
import { Controller, Get } from '@nestjs/common';
@Controller('health')
export class HealthController {
@Get()
check() {
return { status: 'ok' };
}
}
Tip 3 – Monitor Memory Usage
Cheap VPSes often OOM‑kill your process. Install htop and set an alert when RAM > 80%.
Monetization (Optional)
If you found this guide saved you time (or money), consider supporting my newsletter. I regularly publish cheat‑sheets for Node.js, Docker, and low‑cost cloud hacks that help developers like you scale profitably.
Bottom Line: The “Cannot listen on 0.0.0.0:3000” error is usually a timing or port‑conflict issue on low‑end VPSes. By adding a short startup delay, catching EADDRINUSE, and persisting the chosen port, you can get a NestJS API back online in under five minutes—no expensive upgrade required.
No comments:
Post a Comment