JavaScript runtime built on Chrome's V8 JavaScript engine. Essential platform for modern web applications, APIs, and microservices with enterprise-grade performance and security features.
1. Prerequisites
2. Supported Operating Systems
This guide supports installation on:
3. Installation
Using NVM (Recommended for Development)
# Download and install NVM (Node Version Manager)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash
# Reload shell profile
source ~/.bashrc
# Install latest LTS version (recommended for production)
nvm install --lts
nvm use --lts
# Install specific version
nvm install 20.10.0 # Latest LTS as of 2024
nvm use 20.10.0
# Set default version
nvm alias default 20.10.0
# Verify installation
node --version
npm --version
# List available versions
nvm list-remote --lts
nvm list
Ubuntu/Debian (Package Manager)
# Update system packages
sudo apt update && sudo apt upgrade -y
# Method 1: Install from Ubuntu repositories (older version)
sudo apt install -y nodejs npm
# Method 2: Install from NodeSource repository (latest LTS)
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt install -y nodejs
# Install build tools for native modules
sudo apt install -y build-essential python3-dev
# Verify installation
node --version
npm --version
# Update npm to latest version
sudo npm install -g npm@latest
RHEL/CentOS/Rocky Linux/AlmaLinux
# Enable EPEL repository
sudo yum install -y epel-release
# Method 1: Install from EPEL (may be older)
sudo yum install -y nodejs npm
# Method 2: Install from NodeSource (recommended)
curl -fsSL https://rpm.nodesource.com/setup_lts.x | sudo bash -
sudo yum install -y nodejs
# Install development tools
sudo yum groupinstall -y "Development Tools"
sudo yum install -y python3-devel
# Verify installation
node --version
npm --version
Fedora
# Install Node.js from official repositories
sudo dnf install -y nodejs npm
# Or install from NodeSource for latest LTS
curl -fsSL https://rpm.nodesource.com/setup_lts.x | sudo bash -
sudo dnf install -y nodejs
# Install development tools
sudo dnf groupinstall -y "Development Tools"
sudo dnf install -y python3-devel
# Verify installation
node --version
npm --version
Arch Linux
# Install Node.js and npm
sudo pacman -Syu nodejs npm
# Install base-devel for building native modules
sudo pacman -S base-devel python
# Verify installation
node --version
npm --version
Docker Installation
# Create Node.js application structure
mkdir -p ~/nodejs-app/{src,config,logs,node_modules}
cd ~/nodejs-app
# Create production Dockerfile
cat > Dockerfile <<EOF
# Multi-stage build for production
FROM node:20-alpine AS builder
# Create app directory
WORKDIR /usr/src/app
# Copy package files
COPY package*.json ./
# Install dependencies (including dev dependencies)
RUN npm ci --only=production
# Production stage
FROM node:20-alpine AS production
# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
# Set working directory
WORKDIR /usr/src/app
# Copy node_modules from builder stage
COPY --from=builder /usr/src/app/node_modules ./node_modules
# Copy application code
COPY --chown=nodejs:nodejs . .
# Remove unnecessary files
RUN rm -f .dockerignore Dockerfile* README.md
# Security: Run as non-root user
USER nodejs
# Expose port
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node healthcheck.js
# Start application
CMD ["node", "server.js"]
EOF
# Create .dockerignore
cat > .dockerignore <<EOF
node_modules
npm-debug.log
Dockerfile*
.git
.gitignore
README.md
.env
.nyc_output
coverage
.npm
.coverage
.jest
.cache
EOF
# Create production docker-compose.yml
cat > docker-compose.prod.yml <<EOF
version: '3.8'
services:
app:
build:
context: .
target: production
restart: unless-stopped
ports:
- "127.0.0.1:3000:3000"
environment:
- NODE_ENV=production
- PORT=3000
volumes:
- ./logs:/usr/src/app/logs
networks:
- app-network
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /tmp
healthcheck:
test: ["CMD", "node", "healthcheck.js"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
depends_on:
- app
networks:
- app-network
networks:
app-network:
driver: bridge
EOF
docker-compose -f docker-compose.prod.yml up -d
Production Configuration
PM2 Process Manager (Production Standard)
# Install PM2 globally
sudo npm install -g pm2
# Create PM2 ecosystem configuration
cat > ecosystem.config.js <<EOF
module.exports = {
apps: [{
name: 'node-app',
script: './server.js',
instances: 'max', // Use all CPU cores
exec_mode: 'cluster',
// Performance settings
max_memory_restart: '1G',
node_args: '--max-old-space-size=1024',
// Environment variables
env: {
NODE_ENV: 'development',
PORT: 3000
},
env_production: {
NODE_ENV: 'production',
PORT: 3000
},
// Logging
log_file: './logs/app.log',
out_file: './logs/out.log',
error_file: './logs/error.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
merge_logs: true,
// Monitoring
monitoring: true,
pmx: true,
// Auto-restart settings
autorestart: true,
watch: false, // Disable in production
max_restarts: 10,
min_uptime: '10s',
// Instance settings
instance_var: 'INSTANCE_ID',
// Source map support
source_map_support: true,
// Graceful shutdown
kill_timeout: 5000,
listen_timeout: 8000,
// Health checking
health_check_grace_period: 3000
}],
// Deployment configuration
deploy: {
production: {
user: 'nodejs',
host: ['app1.example.com', 'app2.example.com'],
ref: 'origin/main',
repo: 'git@github.com:username/repository.git',
path: '/var/www/production',
'post-deploy': 'npm install && pm2 reload ecosystem.config.js --env production',
'pre-setup': 'apt update -y; apt install git -y'
},
staging: {
user: 'nodejs',
host: 'staging.example.com',
ref: 'origin/develop',
repo: 'git@github.com:username/repository.git',
path: '/var/www/staging',
'post-deploy': 'npm install && pm2 reload ecosystem.config.js --env staging'
}
}
};
EOF
# Start application with PM2
pm2 start ecosystem.config.js --env production
# Save PM2 configuration
pm2 save
# Generate startup script
pm2 startup
sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u $USER --hp $HOME
# Monitor application
pm2 status
pm2 logs
pm2 monit
NGINX Reverse Proxy Configuration
# Create NGINX configuration for Node.js applications
sudo tee /etc/nginx/sites-available/nodejs-app > /dev/null <<EOF
# Node.js Application NGINX Configuration
upstream nodejs_backend {
least_conn;
server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
# Add more servers for load balancing:
# server 127.0.0.1:3001 max_fails=3 fail_timeout=30s;
# server 127.0.0.1:3002 max_fails=3 fail_timeout=30s;
keepalive 64;
}
# HTTP to HTTPS redirect
server {
listen 80;
server_name app.example.com;
return 301 https://\$server_name\$request_uri;
}
# HTTPS server
server {
listen 443 ssl http2;
server_name app.example.com;
# SSL configuration
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options DENY always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Rate limiting
limit_req_zone \$binary_remote_addr zone=api_limit:10m rate=10r/s;
limit_req zone=api_limit burst=20 nodelay;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;
location / {
proxy_pass http://nodejs_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_cache_bypass \$http_upgrade;
# Timeout settings
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# Buffer settings
proxy_buffer_size 4k;
proxy_buffers 4 4k;
}
# Static assets caching
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)\$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# API rate limiting (stricter)
location /api/ {
limit_req zone=api_limit burst=10 nodelay;
proxy_pass http://nodejs_backend;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
# Health check endpoint
location /health {
access_log off;
proxy_pass http://nodejs_backend;
proxy_set_header Host \$host;
}
}
EOF
sudo ln -s /etc/nginx/sites-available/nodejs-app /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
SystemD Service (Alternative to PM2)
# Create systemd service for Node.js application
sudo tee /etc/systemd/system/nodejs-app.service > /dev/null <<EOF
[Unit]
Description=Node.js Application
Documentation=https://nodejs.org/
After=network.target
[Service]
Type=simple
User=nodejs
Group=nodejs
WorkingDirectory=/var/www/nodejs-app
ExecStart=/usr/bin/node server.js
ExecReload=/bin/kill -SIGUSR2 \$MAINPID
Restart=always
RestartSec=10
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=nodejs-app
# Environment variables
Environment=NODE_ENV=production
Environment=PORT=3000
# Security settings
NoNewPrivileges=true
PrivateTmp=true
PrivateDevices=true
ProtectHome=true
ProtectSystem=strict
ReadWritePaths=/var/www/nodejs-app/logs /var/www/nodejs-app/uploads
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
# Resource limits
MemoryMax=2G
CPUQuota=200%
TasksMax=4096
[Install]
WantedBy=multi-user.target
EOF
# Create nodejs user
sudo useradd --system --shell /bin/false --home /var/www/nodejs-app nodejs
# Set up application directory
sudo mkdir -p /var/www/nodejs-app/{logs,uploads}
sudo chown -R nodejs:nodejs /var/www/nodejs-app
sudo systemctl daemon-reload
sudo systemctl enable --now nodejs-app
Security Hardening
Application Security Best Practices
# Create secure application template
mkdir -p ~/secure-nodejs-app
cd ~/secure-nodejs-app
# Create package.json with security-focused dependencies
cat > package.json <<EOF
{
"name": "secure-nodejs-app",
"version": "1.0.0",
"description": "Secure Node.js application template",
"main": "server.js",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js",
"test": "jest",
"security:audit": "npm audit",
"security:check": "nsp check",
"lint": "eslint .",
"format": "prettier --write ."
},
"dependencies": {
"express": "^4.18.2",
"helmet": "^7.1.0",
"cors": "^2.8.5",
"express-rate-limit": "^7.1.5",
"express-validator": "^7.0.1",
"bcryptjs": "^2.4.3",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.0.3",
"dotenv": "^16.3.1",
"compression": "^1.7.4",
"morgan": "^1.10.0",
"winston": "^3.11.0",
"hpp": "^0.2.3",
"express-mongo-sanitize": "^2.2.0",
"xss": "^1.0.14"
},
"devDependencies": {
"nodemon": "^3.0.2",
"jest": "^29.7.0",
"eslint": "^8.55.0",
"prettier": "^3.1.0",
"nsp": "^3.2.1"
},
"engines": {
"node": ">=20.0.0",
"npm": ">=9.0.0"
}
}
EOF
# Create secure server template
cat > server.js <<'EOF'
'use strict';
const express = require('express');
const helmet = require('helmet');
const cors = require('cors');
const rateLimit = require('express-rate-limit');
const { body, validationResult } = require('express-validator');
const mongoSanitize = require('express-mongo-sanitize');
const xss = require('xss');
const compression = require('compression');
const morgan = require('morgan');
const winston = require('winston');
const hpp = require('hpp');
require('dotenv').config();
const app = express();
const PORT = process.env.PORT || 3000;
// Security middleware
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'"],
imgSrc: ["'self'", "data:", "https:"]
}
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
}
}));
// CORS configuration
app.use(cors({
origin: process.env.ALLOWED_ORIGINS?.split(',') || ['https://example.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true
}));
// Rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per windowMs
message: 'Too many requests from this IP, please try again later.',
standardHeaders: true,
legacyHeaders: false
});
app.use(limiter);
// API rate limiting (stricter)
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 50,
message: 'Too many API requests from this IP'
});
app.use('/api/', apiLimiter);
// Body parsing with size limits
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
// Security sanitization
app.use(mongoSanitize());
app.use(hpp()); // Prevent HTTP Parameter Pollution
// Compression
app.use(compression());
// Logging
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'logs/error.log', level: 'error' }),
new winston.transports.File({ filename: 'logs/combined.log' })
]
});
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.simple()
}));
}
// Request logging
app.use(morgan('combined', {
stream: { write: message => logger.info(message.trim()) }
}));
// Input validation middleware
const validateInput = (req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
errors: errors.array()
});
}
next();
};
// Health check endpoint
app.get('/health', (req, res) => {
res.status(200).json({
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage(),
version: process.version
});
});
// API routes with validation
app.post('/api/users', [
body('email').isEmail().normalizeEmail(),
body('name').trim().escape().isLength({ min: 2, max: 50 }),
body('password').isLength({ min: 12 }).matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])/),
validateInput
], (req, res) => {
try {
// Sanitize input
const sanitizedBody = {
email: xss(req.body.email),
name: xss(req.body.name),
password: req.body.password // Don't sanitize password, just validate
};
// Process request
res.json({ success: true, message: 'User created successfully' });
} catch (error) {
logger.error('Error creating user:', error);
res.status(500).json({ success: false, message: 'Internal server error' });
}
});
// Error handling middleware
app.use((error, req, res, next) => {
logger.error('Unhandled error:', error);
res.status(error.status || 500).json({
success: false,
message: process.env.NODE_ENV === 'production'
? 'Something went wrong!'
: error.message
});
});
// 404 handler
app.use('*', (req, res) => {
res.status(404).json({
success: false,
message: 'Route not found'
});
});
// Graceful shutdown
process.on('SIGTERM', () => {
logger.info('SIGTERM signal received: closing HTTP server');
server.close(() => {
logger.info('HTTP server closed');
process.exit(0);
});
});
process.on('SIGINT', () => {
logger.info('SIGINT signal received: closing HTTP server');
server.close(() => {
logger.info('HTTP server closed');
process.exit(0);
});
});
// Unhandled promise rejection handling
process.on('unhandledRejection', (reason, promise) => {
logger.error('Unhandled Rejection at:', promise, 'reason:', reason);
process.exit(1);
});
// Uncaught exception handling
process.on('uncaughtException', (error) => {
logger.error('Uncaught Exception thrown:', error);
process.exit(1);
});
const server = app.listen(PORT, '127.0.0.1', () => {
logger.info(`Server running on port ${PORT} in ${process.env.NODE_ENV} mode`);
});
module.exports = app;
EOF
# Create healthcheck script for Docker
cat > healthcheck.js <<EOF
const http = require('http');
const options = {
hostname: 'localhost',
port: process.env.PORT || 3000,
path: '/health',
method: 'GET',
timeout: 2000
};
const req = http.request(options, (res) => {
if (res.statusCode === 200) {
process.exit(0);
} else {
process.exit(1);
}
});
req.on('error', () => {
process.exit(1);
});
req.end();
EOF
# Install dependencies
npm install
Environment Configuration
# Create secure environment configuration
cat > .env.example <<EOF
# Node.js Application Environment Configuration
# Application settings
NODE_ENV=production
PORT=3000
HOST=127.0.0.1
# Database connection
DATABASE_URL=mongodb://username:password@localhost:27017/myapp?authSource=admin
# Or PostgreSQL: postgresql://username:password@localhost:5432/myapp
# Security keys (generate with: openssl rand -base64 32)
JWT_SECRET=your_jwt_secret_here_32_characters_minimum
SESSION_SECRET=your_session_secret_here_32_characters_minimum
ENCRYPTION_KEY=your_encryption_key_here_32_characters
# CORS settings
ALLOWED_ORIGINS=https://example.com,https://app.example.com
# Email configuration (for notifications)
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=app@example.com
SMTP_PASS=smtp_password
# Rate limiting
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX=100
# File upload settings
MAX_FILE_SIZE=10485760
UPLOAD_PATH=/var/www/nodejs-app/uploads
# Monitoring
ENABLE_MONITORING=true
LOG_LEVEL=info
# Security settings
BCRYPT_ROUNDS=12
JWT_EXPIRY=24h
SESSION_TIMEOUT=3600000
# External APIs
EXTERNAL_API_KEY=your_api_key_here
EXTERNAL_API_URL=https://api.external-service.com
# Redis configuration (for sessions/caching)
REDIS_URL=redis://localhost:6379
REDIS_PASSWORD=redis_password
# Health check settings
HEALTH_CHECK_INTERVAL=30000
EOF
# Create production environment file
cp .env.example .env
echo ".env" >> .gitignore
# Set secure permissions
chmod 600 .env
Performance Optimization
Node.js Performance Tuning
# Create performance optimization script
sudo tee /usr/local/bin/nodejs-optimize.sh > /dev/null <<'EOF'
#!/bin/bash
echo "Optimizing Node.js application performance..."
# System-level optimizations
# Increase file descriptor limits
sudo tee -a /etc/security/limits.conf > /dev/null <<LIMITS
nodejs soft nofile 65535
nodejs hard nofile 65535
nodejs soft nproc 65535
nodejs hard nproc 65535
LIMITS
# Kernel optimizations for Node.js
sudo tee -a /etc/sysctl.conf > /dev/null <<SYSCTL
# Node.js optimizations
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 5000
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_fin_timeout = 10
net.ipv4.tcp_tw_reuse = 1
net.ipv4.ip_local_port_range = 15000 65000
fs.file-max = 100000
vm.swappiness = 1
SYSCTL
sudo sysctl -p
# PM2 optimization for clustering
if command -v pm2 >/dev/null 2>&1; then
# Update PM2 configuration for performance
pm2 delete all 2>/dev/null || true
cat > /tmp/ecosystem-optimized.config.js <<PM2CONFIG
module.exports = {
apps: [{
name: 'nodejs-app-optimized',
script: './server.js',
instances: 'max',
exec_mode: 'cluster',
// V8 optimizations
node_args: [
'--max-old-space-size=2048',
'--optimize-for-size',
'--gc-interval=100',
'--expose-gc'
],
// Performance settings
max_memory_restart: '2G',
min_uptime: '10s',
max_restarts: 5,
// Environment
env_production: {
NODE_ENV: 'production',
UV_THREADPOOL_SIZE: 16
}
}]
};
PM2CONFIG
pm2 start /tmp/ecosystem-optimized.config.js --env production
pm2 save
fi
echo "Node.js optimization completed"
EOF
sudo chmod +x /usr/local/bin/nodejs-optimize.sh
Monitoring and Logging
# Create Node.js monitoring script
sudo tee /usr/local/bin/nodejs-monitor.sh > /dev/null <<'EOF'
#!/bin/bash
MONITOR_LOG="/var/log/nodejs-monitor.log"
log_message() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a ${MONITOR_LOG}
}
# Check if PM2 is managing processes
if command -v pm2 >/dev/null 2>&1; then
PM2_STATUS=$(pm2 jlist | jq -r '.[].pm2_env.status' 2>/dev/null)
if echo "$PM2_STATUS" | grep -q "online"; then
log_message "✓ PM2 processes are running"
# Check memory usage
PM2_MEMORY=$(pm2 jlist | jq -r '.[].monit.memory' 2>/dev/null | awk '{sum += $1} END {print sum/1024/1024}')
log_message "ℹ PM2 total memory usage: ${PM2_MEMORY}MB"
# Check CPU usage
PM2_CPU=$(pm2 jlist | jq -r '.[].monit.cpu' 2>/dev/null | awk '{sum += $1} END {print sum}')
log_message "ℹ PM2 total CPU usage: ${PM2_CPU}%"
else
log_message "⚠ PM2 processes not online"
fi
fi
# Check systemd service (if not using PM2)
if systemctl is-active nodejs-app >/dev/null 2>&1; then
log_message "✓ Node.js systemd service is running"
else
log_message "ℹ Node.js systemd service not active (may be using PM2)"
fi
# Check application health endpoint
if curl -f http://localhost:3000/health >/dev/null 2>&1; then
log_message "✓ Application health endpoint responding"
# Get detailed health info
HEALTH_DATA=$(curl -s http://localhost:3000/health)
UPTIME=$(echo "$HEALTH_DATA" | jq -r '.uptime' 2>/dev/null)
MEMORY_USED=$(echo "$HEALTH_DATA" | jq -r '.memory.rss' 2>/dev/null)
if [ -n "$UPTIME" ]; then
log_message "ℹ Application uptime: ${UPTIME}s"
fi
if [ -n "$MEMORY_USED" ]; then
MEMORY_MB=$((MEMORY_USED / 1024 / 1024))
log_message "ℹ Application memory: ${MEMORY_MB}MB"
fi
else
log_message "✗ Application health endpoint not responding"
fi
# Check Node.js version
NODE_VERSION=$(node --version 2>/dev/null)
if [ -n "$NODE_VERSION" ]; then
log_message "ℹ Node.js version: ${NODE_VERSION}"
fi
# Check npm security audit
if [ -f "package.json" ]; then
AUDIT_RESULT=$(npm audit --audit-level high --json 2>/dev/null)
VULNERABILITIES=$(echo "$AUDIT_RESULT" | jq -r '.metadata.vulnerabilities.total' 2>/dev/null)
if [ -n "$VULNERABILITIES" ] && [ "$VULNERABILITIES" -gt 0 ]; then
log_message "⚠ ${VULNERABILITIES} security vulnerabilities found"
else
log_message "✓ No high/critical security vulnerabilities"
fi
fi
log_message "Node.js monitoring completed"
EOF
sudo chmod +x /usr/local/bin/nodejs-monitor.sh
# Schedule monitoring every 5 minutes
echo "*/5 * * * * root /usr/local/bin/nodejs-monitor.sh" | sudo tee -a /etc/crontab
Backup and Deployment
Application Backup Strategy
sudo tee /usr/local/bin/nodejs-backup.sh > /dev/null <<'EOF'
#!/bin/bash
BACKUP_DIR="/backup/nodejs"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p ${BACKUP_DIR}/{app,config,logs}
echo "Starting Node.js application backup..."
# Backup application code and dependencies
tar --exclude='node_modules' --exclude='logs/*' --exclude='.git' \
-czf ${BACKUP_DIR}/app/nodejs-app-${DATE}.tar.gz \
-C /var/www/nodejs-app .
# Backup PM2 configuration and process list
if command -v pm2 >/dev/null 2>&1; then
pm2 save
cp ~/.pm2/dump.pm2 ${BACKUP_DIR}/config/pm2-processes-${DATE}.json
cp ecosystem.config.js ${BACKUP_DIR}/config/ecosystem-${DATE}.js 2>/dev/null || true
fi
# Backup environment configuration
cp /var/www/nodejs-app/.env ${BACKUP_DIR}/config/env-${DATE}.backup 2>/dev/null || true
# Backup systemd service files
cp /etc/systemd/system/nodejs-app.service ${BACKUP_DIR}/config/nodejs-app-${DATE}.service 2>/dev/null || true
# Backup logs
tar -czf ${BACKUP_DIR}/logs/nodejs-logs-${DATE}.tar.gz \
/var/www/nodejs-app/logs/ 2>/dev/null || true
# Backup NGINX configuration
cp /etc/nginx/sites-available/nodejs-app ${BACKUP_DIR}/config/nginx-${DATE}.conf 2>/dev/null || true
# Upload to cloud storage
aws s3 cp ${BACKUP_DIR}/ s3://nodejs-backups/ --recursive
gsutil cp -r ${BACKUP_DIR}/* gs://nodejs-backups/
# Keep only last 14 backups
find ${BACKUP_DIR} -name "nodejs-*" -type f -mtime +14 -delete
echo "Node.js backup completed: ${DATE}"
EOF
sudo chmod +x /usr/local/bin/nodejs-backup.sh
# Schedule daily backups
echo "0 2 * * * root /usr/local/bin/nodejs-backup.sh" | sudo tee -a /etc/crontab
Zero-Downtime Deployment
# Create deployment script
sudo tee /usr/local/bin/nodejs-deploy.sh > /dev/null <<'EOF'
#!/bin/bash
VERSION="${1}"
REPO_URL="${2:-https://github.com/example/nodejs-app.git}"
if [ -z "$VERSION" ]; then
echo "Usage: $0 <version> [repo_url]"
echo "Example: $0 v1.2.3"
exit 1
fi
DEPLOY_DIR="/var/www/nodejs-app"
RELEASES_DIR="${DEPLOY_DIR}/releases"
SHARED_DIR="${DEPLOY_DIR}/shared"
CURRENT_LINK="${DEPLOY_DIR}/current"
RELEASE_DIR="${RELEASES_DIR}/${VERSION}"
echo "Deploying Node.js application version: ${VERSION}"
# Create directory structure
mkdir -p ${RELEASES_DIR} ${SHARED_DIR}/{logs,uploads,config}
# Clone and checkout specific version
git clone ${REPO_URL} ${RELEASE_DIR}
cd ${RELEASE_DIR}
git checkout ${VERSION}
# Copy shared configuration
cp ${SHARED_DIR}/config/.env . 2>/dev/null || true
# Install dependencies (production only)
npm ci --only=production
# Run security audit
npm audit --audit-level high
# Run tests
npm test
# Create symbolic links for shared directories
ln -sfn ${SHARED_DIR}/logs logs
ln -sfn ${SHARED_DIR}/uploads uploads
# Update current symlink (atomic operation)
ln -sfn ${RELEASE_DIR} ${CURRENT_LINK}
# Reload PM2 or systemd service
if command -v pm2 >/dev/null 2>&1 && pm2 list | grep -q "nodejs-app"; then
echo "Reloading PM2 processes..."
cd ${CURRENT_LINK}
pm2 reload ecosystem.config.js --env production
elif systemctl is-active nodejs-app >/dev/null 2>&1; then
echo "Restarting systemd service..."
systemctl restart nodejs-app
fi
# Wait for application to be ready
echo "Waiting for application to start..."
for i in {1..30}; do
if curl -f http://localhost:3000/health >/dev/null 2>&1; then
echo "✓ Application is healthy"
break
fi
sleep 2
done
# Keep only last 5 releases
cd ${RELEASES_DIR}
ls -t | tail -n +6 | xargs rm -rf
# Set proper ownership
chown -R nodejs:nodejs ${DEPLOY_DIR}
echo "Deployment completed successfully: ${VERSION}"
echo "Application is available at: http://localhost:3000"
EOF
sudo chmod +x /usr/local/bin/nodejs-deploy.sh
6. Troubleshooting
Common Issues and Solutions
# Check Node.js application status
# PM2 processes
pm2 status
pm2 logs
pm2 monit
# SystemD service
sudo systemctl status nodejs-app
sudo journalctl -u nodejs-app -f
# Check application logs
tail -f /var/www/nodejs-app/logs/combined.log
tail -f /var/www/nodejs-app/logs/error.log
# Test application health
curl -i http://localhost:3000/health
# Check memory leaks
node --inspect server.js
# Connect Chrome DevTools to memory tab
# Performance profiling
node --prof server.js
# Generate profile: node --prof-process isolate-*.log > profile.txt
# Check for security vulnerabilities
npm audit
npm audit fix
# Update dependencies
npm outdated
npm update
# Check package vulnerabilities
npx retire
# Memory usage analysis
ps aux --sort=-%mem | grep node
pmap -x $(pgrep node)
# Network connectivity
ss -tulpn | grep 3000
netstat -tulpn | grep node
# Process monitoring
htop -p $(pgrep node)
top -p $(pgrep node)
# Debug mode (development only)
node --inspect-brk server.js
# Cluster mode debugging
pm2 logs --lines 1000
pm2 flush # Clear all logs
# Database connection issues
# Check MongoDB connection
mongosh --eval "db.adminCommand('ping')"
# Check PostgreSQL connection
psql -h localhost -U username -d dbname -c "SELECT version();"
# SSL/HTTPS issues
openssl s_client -connect app.example.com:443
curl -I https://app.example.com
# Environment issues
printenv | grep NODE
echo $NODE_ENV
# Restart applications
pm2 restart all
# Or
sudo systemctl restart nodejs-app
# Clean npm cache
npm cache clean --force
# Rebuild native modules
npm rebuild
Additional Resources
---
Note: This guide is part of the HowToMgr collection.