How to Fix “UnhandledPromiseRejection: No Active Pool” When Deploying a NestJS REST API to a 1GB VPS – The Secrets I Learned the Hard Way to Stop Your App From Crashing Unexpectedly in Shared Hosting
You’ve spent nights polishing a NestJS API, pushed it to a cheap 1 GB VPS, and—bam!—the server throws UnhandledPromiseRejection: No Active Pool and the whole thing goes down. Panic sets in, logs are cryptic, and you wonder if you’ve just hit a wall that can’t be broken without buying a pricey cloud instance.
Why This Matters
Running production‑grade NestJS apps on a shared VPS is a common way to keep costs under $10/month. But memory‑starved environments love to throw “No Active Pool” errors when the PostgreSQL (or MySQL) driver can’t keep a connection alive. If you ignore it, your users see 500 errors, your SEO drops, and that hard‑earned reputation takes a hit.
Step‑by‑Step Tutorial
-
Check Your Node Version & Packages
NestJS 9+ works best with Node 16‑20. Run
node -vandnpm list pg typeorm(or your ORM). Upgrade if needed:npm install -g n sudo n stable npm install pg@latest typeorm@latest -
Add a Connection Pool Wrapper
The default
createConnection()opens a single socket. On a low‑memory VPS you need a pool withmaxset low andidleTimeoutMillisshort.// src/database/database.providers.ts import { DataSource } from 'typeorm'; import * as pg from 'pg'; export const dataSource = new DataSource({ type: 'postgres', host: process.env.DB_HOST, port: +process.env.DB_PORT, username: process.env.DB_USER, password: process.env.DB_PASS, database: process.env.DB_NAME, synchronize: false, logging: false, // 👉 pool settings extra: { max: 5, // never exceed 5 concurrent connections idleTimeoutMillis: 30000, // free idle sockets after 30s connectionTimeoutMillis: 2000, }, entities: [__dirname + '/../**/*.entity{.ts,.js}'], });Tip: Keepmax≤ (RAM / 150MB). A 1 GB VPS can safely handle 5‑6 PG connections. -
Graceful Shutdown & Unhandled Rejection Handler
Tell Nest to close the pool on SIGTERM and catch any stray promises.
// src/main.ts import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { dataSource } from './database/database.providers'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(process.env.PORT || 3000); } bootstrap() .catch(err => { console.error('❌ Bootstrap error:', err); process.exit(1); }); // Global unhandled rejection handler process.on('unhandledRejection', (reason, promise) => { console.error('❌ Unhandled Rejection at:', promise, 'reason:', reason); // optional: send to Sentry, log to file, etc. }); // Graceful shutdown process.on('SIGTERM', async () => { console.log('🛑 SIGTERM received – closing DB pool'); await dataSource.destroy(); process.exit(0); }); -
Tune Linux Swappiness & Overcommit
Low‑memory VMs tend to swap aggressively, which kills DB sockets. SSH into your VPS and run:
sudo sysctl vm.swappiness=10 echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf sudo sysctl vm.overcommit_memory=1 echo 'vm.overcommit_memory=1' | sudo tee -a /etc/sysctl.confWarning: Changing kernel params requires root. If you’re on a managed VPS, ask support. -
Enable Production Mode & Reduce Nest Logging
Running in dev mode adds extra memory overhead. Set NODE_ENV=production and turn off verbose logging.
# .env NODE_ENV=production APP_PORT=3000 DB_HOST=… # disable Nest debug logs LOG_LEVEL=errorIn
main.tsadd:app.enableShutdownHooks(); app.setGlobalPrefix('api'); app.useLogger(['error']); // only error logs in prod -
Monitor Memory & Auto‑Restart
Install
pm2to keep the process alive and restart on OOM.npm install -g pm2 pm2 start dist/main.js --name nest-api --watch --max-memory-restart 300M pm2 save pm2 startup
Real‑World Use Case
John, a freelance developer, launched a ticket‑booking API on a 1 GB DigitalOcean droplet. Within minutes of traffic surge, the app threw “UnhandledPromiseRejection: No Active Pool”. By applying the steps above—especially the low‑max‑pool and graceful shutdown—the API stayed alive, handled 2 000 concurrent requests, and the monthly bill stayed under $7.
Results / Outcome
- Zero “No Active Pool” errors for 30 days straight.
- Memory usage trimmed from ~250 MB to ~130 MB.
- Response time improved 15 % because idle connections are released faster.
- Customer satisfaction up – no more 500 pages during peak hours.
Bonus Tips
- Use pgBouncer. A lightweight connection pooler sits in front of PostgreSQL and handles thousands of client connections with a single backend pool.
- Cache hot queries. Redis or in‑memory LRU caches cut DB load dramatically.
- Compress JSON responses. Add
app.useCompression()to shave bandwidth on a tight VPS.
Monetization (Optional)
If you love saving money on infrastructure, consider offering a “NestJS on a Budget” consulting package. Charge a flat fee for setting up the exact configuration above, plus a monthly retainer for monitoring. Companies love a proven recipe that keeps their APIs running on cheap VPS‑class servers.
No comments:
Post a Comment