Deploying Full-Stack Applications (NestJS & React) on Linux VPS with Nginx and PM2
Comprehensive Guide: Deploying Full-Stack Applications (NestJS & React) on Linux VPS with Nginx and PM2
Moving an application from a local environment to a production server is a critical milestone in the software development lifecycle. To ensure a stable, self-healing, and secure system, developers must master core Linux tools. This article provides a detailed walkthrough on setting up a Node.js v22 environment, configuring Nginx as a Reverse Proxy, and managing operations with PM2 for NestJS (Backend) and React (Frontend) projects.
1. Environment Preparation: Installing Node.js v22 (LTS)
Node.js v22 introduces significant performance improvements and enhanced ES Modules compatibility. For Ubuntu/Debian installations, using NodeSource is recommended to ensure you receive the latest stable updates.
// Environment version check structure before Deployment
interface EnvSpecs {
nodeVersion: string;
requiredVersion: string;
os: string;
}
function checkEnvironment(specs: EnvSpecs): boolean {
const current = parseInt(specs.nodeVersion.split('.')[0]);
const required = parseInt(specs.requiredVersion.split('.')[0]);
if (current < required) {
console.error(`Error: Node.js ${specs.requiredVersion} required. Current is ${specs.nodeVersion}`);
return false;
}
console.log("Environment valid. Starting build process...");
return true;
}
const deployEnv: EnvSpecs = { nodeVersion: "22.0.0", requiredVersion: "22.0.0", os: "Ubuntu 24.04" };
checkEnvironment(deployEnv);
Run the following commands in your terminal to install:
- Update system:
sudo apt update && sudo apt upgrade -y - Install Node.js:
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - && sudo apt-get install -y nodejs
2. Process Management with PM2 - Ensuring Zero-Downtime
PM2 is the most powerful process manager for Node.js. It ensures your NestJS application automatically restarts upon a crash and provides centralized log management.
// ecosystem.config.js configuration for a NestJS project
module.exports = {
apps: [{
name: "nestjs-backend",
script: "./dist/main.js",
instances: "max", // Cluster mode to utilize all CPU cores
exec_mode: "cluster",
env: {
NODE_ENV: "production",
PORT: 3000
},
log_date_format: "YYYY-MM-DD HH:mm Z",
error_file: "./logs/error.log",
out_file: "./logs/out.log"
}]
}
Essential PM2 commands to remember:
pm2 start ecosystem.config.js: Launch the application according to configuration.pm2 monit: Monitor real-time RAM/CPU consumption of processes.pm2 save: Save the process list to auto-start with the OS.
3. Configuring Nginx as a Reverse Proxy
Nginx acts as the "gatekeeper," receiving HTTP/HTTPS requests from users and routing them to the NestJS application (Backend) or serving static files for React (Frontend).
| Feature | Frontend (React) | Backend (NestJS) |
|---|---|---|
| Serving Mechanism | Static Files (HTML/JS/CSS) | API Proxy (Port 3000) |
| Nginx Directive | root /var/www/react-app/dist; |
proxy_pass http://localhost:3000; |
| Optimization | Gzip Compression, Browser Caching | Rate Limiting, Header Security |
// Mock Nginx configuration via TypeScript Interface
interface NginxConfig {
domain: string;
backendPort: number;
sslEnabled: boolean;
staticPath: string;
}
const webConfig: NginxConfig = {
domain: "api.example.com",
backendPort: 3000,
sslEnabled: true,
staticPath: "/var/www/frontend/dist"
};
console.log(`Configuring Proxy for domain ${webConfig.domain} to port ${webConfig.backendPort}`);
4. Build Workflow and React Frontend Optimization
Unlike the Backend, React must be compiled into client-side code. Vite is commonly used to optimize build speeds.
After running npm run build, the source code will reside in the /dist folder. You must upload this folder to the VPS and point Nginx to it. Ensure try_files is configured to support React Router:
/* Nginx configuration for React Router (Logic Simulation) */
const nginxTryFiles = "try_files $uri $uri/ /index.html";
// This line ensures that when a user refreshes on /dashboard,
// Nginx returns index.html instead of a 404 error.
5. System Security with SSL (Certbot) and Firewall
Modern applications must run on HTTPS. We utilize Let's Encrypt via the Certbot tool for automated SSL management.
- Install Certbot:
sudo apt install certbot python3-certbot-nginx - Enable SSL:
sudo certbot --nginx -d yourdomain.com - Firewall Configuration (UFW): Open only necessary ports like 80, 443, and a custom SSH port.
// Security Header Validation function
function validateSecurityHeaders(headers: Record): boolean {
const requiredHeaders = ['strict-transport-security', 'x-frame-options', 'content-security-policy'];
return requiredHeaders.every(h => headers.hasOwnProperty(h));
}
const currentHeaders = {
'strict-transport-security': 'max-age=31536000',
'x-content-type-options': 'nosniff'
};
console.log(`Security compliance: ${validateSecurityHeaders(currentHeaders)}`);
6. Log Management and System Health Monitoring
When an application runs in Production, logs are your only way to trace errors. Implement Log Rotation to prevent log files from filling up the VPS storage.
PM2 supports pm2-logrotate for automation. Additionally, set up a Cronjob to clean temporary directories periodically.
// Simple Monitoring Configuration Interface
interface HealthCheck {
serviceName: string;
uptime: number;
memoryUsage: number; // MB
status: 'ALIVE' | 'DEAD';
}
function reportSystemStatus(check: HealthCheck) {
if (check.memoryUsage > 1024) {
console.warn(`Warning: Service ${check.serviceName} is consuming excessive RAM!`);
}
console.log(`Service: ${check.serviceName} | Uptime: ${check.uptime}s | Status: ${check.status}`);
}
reportSystemStatus({ serviceName: "NestJS-API", uptime: 86400, memoryUsage: 450, status: 'ALIVE' });
7. Conclusion: Deployment Success Checklist
To ensure your Full-stack project runs smoothly on a Linux VPS, always go through this checklist before hand-off:
- Are environment variables (.env) configured specifically for Production (DB, API Keys)?
- Has Nginx been configured with an upload size limit (client_max_body_size)?
- Is PM2 set to
startupto run automatically after a VPS reboot? - Has the SSL Certificate auto-renewal been verified?
- Are database ports (PostgreSQL/MySQL) closed to external access?
Deploying an application is an art of coordinating Code and Infrastructure. Happy building!
