Fixing “Unhandled Promise Rejection: Connection Refused” When Deploying a NestJS API to a Shared Hosting VPS – A Developer’s Urgent Guide to mDNS, Port Conflicts and Corrupt Runtime Environments
You’ve just pushed your sleek NestJS API to a cheap VPS, hit “deploy” and—in a flash—your console explodes with “Unhandled Promise Rejection: Connection Refused”. The error feels like a brick wall, and every retry just adds more noise. If you’re tired of hunting down phantom ports, fighting hidden mDNS services, or wondering why the runtime “just died” on a shared host, keep reading. This guide walks you through the exact fixes, step‑by‑step, so you can get your API back online in minutes and stop losing billable development hours.
Why This Matters
Every minute your API is down is a lost opportunity—whether that’s a paying customer hitting an endpoint, a webhook that never fires, or a cron job that stalls. On shared VPS plans, hidden services (like avahi‑daemon) can hijack the default 3000 port, while mis‑configured .env files silently point to the wrong database. The result? A dreaded ECONNREFUSED that looks like a code bug but is actually an environment problem.
Fixing it not only restores uptime, it teaches you how to future‑proof your deployments, avoid costly support tickets, and keep your dev pipeline humming.
Step‑by‑Step Tutorial
-
Confirm the Runtime Environment
Log into your VPS and verify Node.js and npm versions. Incompatible versions are a silent killer.
node -v npm -v # Expected: Node 18.x or higher for Nest 9+Tip: If the version is outdated, install the latest LTS withnvm install --ltsand set it as default. -
Check for Port Conflicts (mDNS & Shared Services)
Many shared hosts run
avahi-daemonorsystemd-resolvedthat bind to0.0.0.0:3000for mDNS discovery. Usenetstatorssto see who’s listening.sudo ss -tulpn | grep :3000 # Example output: # LISTEN 0 128 0.0.0.0:3000 0.0.0.0:* users:(("avahi-daemon",pid=785,fd=5))Warning: Never force‑kill a system service; you’ll break DNS on the whole server. Instead, change your NestJS port (see step 5) or stop the service safely if you control the VPS. -
Validate Your .env File
Missing or malformed variables are the most common cause of
ECONNREFUSEDwhen connecting to a database or Redis.# .env (example) DB_HOST=127.0.0.1 DB_PORT=5432 DB_USER=nest_user DB_PASS=super_secret DB_NAME=nest_prod # Ensure no stray spaces or Windows line endings!Tip: Runcat -A .envto reveal hidden^Mcharacters that break parsing. -
Test Database Connectivity From the VPS
Before Nest starts, confirm you can talk to the DB with a simple
psql(Postgres) ormysqlclient.# Postgres example PGPASSWORD=$DB_PASS psql -h $DB_HOST -U $DB_USER -d $DB_NAME -p $DB_PORT -c "\l"Tip: If the client fails, the problem is outside Nest – fix firewall rules, bind addresses, or DB user permissions. -
Change the NestJS Listening Port
Pick a high, unused port (e.g.,
5000) to dodge hidden services.// main.ts async function bootstrap() { const app = await NestFactory.create(AppModule); const port = process.env.PORT || 5000; // ← changed await app.listen(port, () => console.log(`🚀 API listening on ${port}`)); } bootstrap(); -
Restart the Application with Proper Logging
Use
pm2or a systemd service that capturesstderr. This ensures unhandled rejections surface in the logs.# pm2 ecosystem file (ecosystem.config.js) module.exports = { apps: [{ name: "nest-api", script: "dist/main.js", interpreter: "node", env: { NODE_ENV: "production", PORT: 5000 }, watch: false, error_file: "/var/log/nest-api/error.log", out_file: "/var/log/nest-api/out.log" }] };Tip: Addprocess.on('unhandledRejection', err => { console.error(err); process.exit(1); });to force a crash and restart on fatal errors. -
Verify the Fix
From your local machine, hit the new endpoint.
curl -i http://your-vps-ip:5000/health # Expected: 200 OK with JSON { "status":"ok" }
Real‑World Use Case: E‑Commerce Checkout API
A small Shopify‑integrated startup deployed a NestJS checkout microservice on a $5/month VPS. After the first sale, the webhook from Shopify failed with “Connection Refused”. The root cause was the VPS’s default avahi-daemon occupying port 3000. By moving the API to 5000, updating the webhook URL, and locking the Node version with nvm, the checkout flow recovered in under 30 minutes, saving the team an estimated $250 in lost sales.
Results / Outcome
- Zero “Unhandled Promise Rejection” errors in production logs.
- Stable database connections verified with command‑line tools.
- Fast, repeatable deployment script that works on any shared VPS.
- Immediate revenue protection for time‑critical APIs.
Bonus Tips
- Automate Environment Checks: Add a pre‑start script in
package.jsonthat runsnode scripts/check-env.jsto abort if any required variable is missing. - Use Docker Even on VPS: A lightweight
node:18-alpinecontainer isolates your app from host‑level services, eliminating most port‑conflict surprises. - Health‑Check Endpoint: Expose
/healththat returns DB connectivity status. Pair it with a monitoring service (UptimeRobot, Healthchecks.io). - Log Rotation: Set up
logrotatefor/var/log/nest-api/*.logto keep disk usage low on cheap plans.
Monetize Your Knowledge (Optional)
If you found this guide helpful, consider turning your troubleshooting expertise into a side hustle:
- Write a paid “NestJS on Shared Hosting” ebook and sell it on Gumroad.
- Offer a one‑hour consulting call for $75 to debug production APIs.
- Create a short video course on Udemy focusing on “Deploying Node.js Apps to Low‑Cost VPS”.
These extra revenue streams can quickly offset your hosting costs and give you more freedom to experiment.
No comments:
Post a Comment