Despliegue de Aplicaciones Node.js con PM2: Guía Completa de Producción
Introducción
PM2 (Process Manager 2) es un gestor de procesos de grado de producción para aplicaciones Node.js que proporciona reinicios automáticos, balanceo de carga, monitoreo y capacidades de despliegue. Es el estándar de la industria para mantener aplicaciones Node.js vivas para siempre, gestionar registros de aplicaciones y asegurar despliegues sin tiempo de inactividad. Esta guía completa cubre todo desde la instalación básica de PM2 hasta despliegues avanzados en producción, monitoreo y solución de problemas.
Lo Que Aprenderás
- Instalación y configuración completa de PM2
- Despliegue y gestión de procesos de aplicaciones
- Modo cluster y balanceo de carga
- Despliegues y actualizaciones sin tiempo de inactividad
- Gestión de registros y monitoreo
- Scripts de inicio y recuperación automática
- Configuración de archivos ecosystem de PM2
- Optimización de rendimiento
- Integración con herramientas de monitoreo
- Solución de problemas comunes
¿Por Qué PM2?
- Gestión de Procesos: Mantiene aplicaciones ejecutándose para siempre
- Balanceo de Carga: Modo cluster integrado para CPUs multi-núcleo
- Sin Tiempo de Inactividad: Recarga aplicaciones sin interrupciones
- Monitoreo: Monitoreo de aplicaciones en tiempo real
- Gestión de Registros: Recopilación y rotación centralizadas de registros
- Scripts de Inicio: Inicio automático al reiniciar el servidor
- Despliegue Fácil: Flujo de trabajo de despliegue simple
- Gestión de Recursos: Optimización de uso de memoria y CPU
Requisitos Previos
- Node.js y npm instalados (ver guía de instalación de Node.js)
- Ubuntu 20.04+, Debian 10+, CentOS 8+ o Rocky Linux 8+
- Acceso root o sudo
- Aplicación básica de Node.js lista para despliegue
- Al menos 512MB de RAM (se recomienda 1GB+)
Instalación
Instalar PM2 Globalmente
# Instalar PM2
npm install pm2 -g
# Verificar instalación
pm2 --version
# Verificar que PM2 esté en PATH
which pm2
# Si hay errores de permisos, ver sección de solución de problemas
Actualizar PM2
# Actualizar PM2 a la última versión
npm install pm2@latest -g
# Actualizar daemon de PM2
pm2 update
Configuración
Gestión Básica de Aplicaciones
Iniciar Aplicaciones
# Iniciar aplicación simple de Node.js
pm2 start app.js
# Iniciar con nombre personalizado
pm2 start app.js --name "my-api"
# Iniciar con modo watch (reinicio automático al cambiar archivos)
pm2 start app.js --watch
# Iniciar con argumentos específicos de Node.js
pm2 start app.js --node-args="--max-old-space-size=4096"
# Iniciar script npm
pm2 start npm --name "my-app" -- start
# Iniciar con variables de entorno
pm2 start app.js --env production
Gestionar Aplicaciones
# Listar todas las aplicaciones
pm2 list
pm2 ls
# Mostrar información detallada
pm2 show my-api
# Detener aplicación
pm2 stop my-api
pm2 stop 0
# Reiniciar aplicación
pm2 restart my-api
# Recargar aplicación (sin tiempo de inactividad)
pm2 reload my-api
# Eliminar aplicación de PM2
pm2 delete my-api
# Detener todas las aplicaciones
pm2 stop all
# Reiniciar todas las aplicaciones
pm2 restart all
# Eliminar todas las aplicaciones
pm2 delete all
Modo Cluster para Balanceo de Carga
El modo cluster ejecuta múltiples instancias de tu aplicación para utilizar todos los núcleos de CPU:
# Iniciar en modo cluster con CPUs máximas
pm2 start app.js -i max
# Iniciar con número específico de instancias
pm2 start app.js -i 4
# Iniciar y auto-escalar según uso de CPU
pm2 start app.js -i max --max-memory-restart 300M
# Escalar hacia arriba o abajo
pm2 scale my-api +2 # Agregar 2 instancias más
pm2 scale my-api 4 # Establecer exactamente 4 instancias
Archivo Ecosystem de PM2
Crea un archivo ecosystem.config.js para configuraciones complejas:
# Generar archivo ecosystem
pm2 ecosystem
# O crear manualmente
cat > ecosystem.config.js <<'EOF'
module.exports = {
apps: [
{
name: 'api-server',
script: './app.js',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'development',
PORT: 3000
},
env_production: {
NODE_ENV: 'production',
PORT: 8080
},
error_file: './logs/err.log',
out_file: './logs/out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
max_memory_restart: '1G',
node_args: '--max-old-space-size=4096',
watch: false,
ignore_watch: ['node_modules', 'logs'],
max_restarts: 10,
min_uptime: '10s'
},
{
name: 'worker',
script: './worker.js',
instances: 2,
exec_mode: 'cluster',
cron_restart: '0 0 * * *', // Reiniciar diariamente a medianoche
env: {
NODE_ENV: 'development',
WORKER_TYPE: 'background'
},
env_production: {
NODE_ENV: 'production',
WORKER_TYPE: 'background'
}
}
]
};
EOF
# Iniciar usando archivo ecosystem
pm2 start ecosystem.config.js
# Iniciar con entorno específico
pm2 start ecosystem.config.js --env production
# Actualizar aplicaciones desde archivo ecosystem
pm2 reload ecosystem.config.js
Opciones de Configuración Avanzadas
ecosystem.config.js completo con todas las opciones:
module.exports = {
apps: [{
// Configuración básica
name: 'my-app',
script: './app.js',
cwd: '/var/www/my-app',
// Configuración de instancias
instances: 'max',
exec_mode: 'cluster',
// Variables de entorno
env: {
NODE_ENV: 'development',
PORT: 3000,
DATABASE_URL: 'mongodb://localhost:27017/dev'
},
env_production: {
NODE_ENV: 'production',
PORT: 8080,
DATABASE_URL: 'mongodb://production-db:27017/prod'
},
// Registros
error_file: './logs/err.log',
out_file: './logs/out.log',
log_file: './logs/combined.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
merge_logs: true,
// Límites de recursos
max_memory_restart: '1G',
max_restarts: 10,
min_uptime: '10s',
listen_timeout: 3000,
kill_timeout: 5000,
// Configuración de Node.js
node_args: '--max-old-space-size=2048',
// Configuración de watch
watch: false,
ignore_watch: ['node_modules', 'logs', '*.log'],
watch_options: {
followSymlinks: false
},
// Configuración de reinicio
autorestart: true,
cron_restart: '0 0 * * *',
restart_delay: 4000,
// Características avanzadas
source_map_support: true,
instance_var: 'INSTANCE_ID',
// Comandos post-despliegue
post_update: ['npm install', 'echo Despliegue finalizado']
}]
};
Despliegue
Flujo de Trabajo de Despliegue Simple
# Despliegue inicial
pm2 start app.js --name "production-api" -i max
pm2 save
pm2 startup
# Actualizar despliegue
git pull origin main
npm install --production
pm2 reload production-api
# O con archivo ecosystem
pm2 start ecosystem.config.js --env production
pm2 save
Despliegue Sin Tiempo de Inactividad
# Crear script de despliegue
cat > deploy.sh <<'EOF'
#!/bin/bash
echo "Iniciando despliegue..."
# Obtener último código
git pull origin main
# Instalar dependencias
npm install --production
# Ejecutar migraciones de base de datos (si es necesario)
npm run migrate
# Recargar aplicación con cero tiempo de inactividad
pm2 reload ecosystem.config.js --env production
# Verificar estado
pm2 status
echo "¡Despliegue completado exitosamente!"
EOF
chmod +x deploy.sh
# Ejecutar despliegue
./deploy.sh
Configuración de Script de Inicio
Configura PM2 para iniciar aplicaciones al reiniciar el servidor:
# Generar script de inicio
pm2 startup
# Esto mostrará un comando como:
# sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u username --hp /home/username
# Copiar y ejecutar el comando generado
# Guardar lista actual de procesos de PM2
pm2 save
# Probar reiniciando
sudo reboot
# Después del reinicio, verificar que PM2 esté en ejecución
pm2 list
Despliegue Blue-Green
Crea una estrategia de despliegue blue-green:
cat > ecosystem-blue.config.js <<EOF
module.exports = {
apps: [{
name: 'app-blue',
script: './app.js',
instances: 'max',
exec_mode: 'cluster',
env: {
PORT: 3000,
NODE_ENV: 'production'
}
}]
};
EOF
cat > ecosystem-green.config.js <<EOF
module.exports = {
apps: [{
name: 'app-green',
script: './app.js',
instances: 'max',
exec_mode: 'cluster',
env: {
PORT: 3001,
NODE_ENV: 'production'
}
}]
};
EOF
# Desplegar green mientras blue está en ejecución
pm2 start ecosystem-green.config.js
# Cambiar Nginx/Load balancer a green
# Una vez estable, detener blue
pm2 stop app-blue
Aplicación Express de Ejemplo
Crea una aplicación Express de ejemplo para pruebas:
# Crear directorio de aplicación
mkdir -p /var/www/my-api
cd /var/www/my-api
# Inicializar proyecto npm
npm init -y
# Instalar dependencias
npm install express
# Crear app.js
cat > app.js <<'EOF'
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
// Endpoint de verificación de salud
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
uptime: process.uptime(),
timestamp: new Date().toISOString(),
pid: process.pid
});
});
// Endpoint principal
app.get('/', (req, res) => {
res.json({
message: 'API en ejecución',
version: '1.0.0',
instance: process.env.INSTANCE_ID || 0
});
});
// Manejo de errores
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: '¡Algo salió mal!' });
});
app.listen(port, () => {
console.log(`Servidor ejecutándose en puerto ${port}`);
});
// Apagado graceful
process.on('SIGINT', () => {
console.log('Apagando gracefully...');
process.exit(0);
});
EOF
# Iniciar con PM2
pm2 start app.js -i max --name "my-api"
pm2 save
Monitoreo
Monitoreo en Tiempo Real
# Panel de monitoreo en tiempo real
pm2 monit
# Lista simple con métricas
pm2 list
# Información detallada sobre aplicación específica
pm2 show my-api
# Ver registros en tiempo real
pm2 logs
# Ver registros de aplicación específica
pm2 logs my-api
# Ver solo registros de errores
pm2 logs --err
# Ver últimas 100 líneas
pm2 logs --lines 100
# Registros en formato JSON
pm2 logs --json
Gestión de Registros
# Vaciar todos los registros
pm2 flush
# Recargar todos los registros
pm2 reloadLogs
# Instalar módulo de rotación de registros de PM2
pm2 install pm2-logrotate
# Configurar rotación de registros
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7
pm2 set pm2-logrotate:compress true
pm2 set pm2-logrotate:dateFormat YYYY-MM-DD_HH-mm-ss
pm2 set pm2-logrotate:workerInterval 30
pm2 set pm2-logrotate:rotateInterval '0 0 * * *'
Monitoreo de Rendimiento
# Uso de CPU y memoria
pm2 list
# Mostrar métricas detalladas
pm2 describe my-api
# Monitorear métricas específicas
pm2 monitor my-api
# Verificar fugas de memoria
pm2 start app.js --max-memory-restart 500M
# Perfilar aplicación
pm2 profile my-api
# Esperar para perfilado
pm2 profile:stop
Integración con PM2 Plus (Keymetrics)
Para monitoreo avanzado en producción:
# Instalar PM2 Plus
pm2 install pm2-server-monit
# Vincular a cuenta de PM2 Plus (opcional)
pm2 link [secret_key] [public_key] [machine_name]
# Monitorear métricas personalizadas en código
const pmx = require('@pm2/io');
const metric = pmx.metric({
name: 'Usuarios en tiempo real',
value: () => {
return userCount;
}
});
// Acciones personalizadas
pmx.action('Limpiar caché', (reply) => {
clearCache();
reply({ success: true });
});
Solución de Problemas
Aplicación No Inicia
# Verificar registros de PM2
pm2 logs my-api --err
# Verificar aplicación con registros detallados
pm2 start app.js --log-date-format="YYYY-MM-DD HH:mm:ss"
# Verificar si el puerto ya está en uso
sudo lsof -i :3000
# Matar proceso en puerto
sudo kill -9 $(sudo lsof -t -i:3000)
# Iniciar con modo watch para depuración
pm2 start app.js --watch --ignore-watch="node_modules"
Fugas de Memoria
# Establecer umbral de reinicio de memoria
pm2 start app.js --max-memory-restart 500M
# Monitorear uso de memoria
pm2 monit
# Perfilar uso de memoria
pm2 profile my-api
# Habilitar snapshot de heap
pm2 start app.js --node-args="--expose-gc"
# Tomar snapshot de heap
kill -USR2 $(pm2 pid my-api)
Alto Uso de CPU
# Verificar uso de CPU
pm2 list
# Reducir número de instancias
pm2 scale my-api 2
# Perfilar uso de CPU
pm2 profile:cpu my-api
# Esperar...
pm2 profile:cpu:stop
# Verificar bucles infinitos en código
pm2 logs my-api --lines 1000
Caídas de Aplicación
# Ver registros de caídas
pm2 logs my-api --err --lines 100
# Aumentar intentos de reinicio
pm2 start app.js --max-restarts 10 --min-uptime 10000
# Agregar manejo de errores
pm2 start app.js --error /var/log/app-error.log
# Verificar excepciones no capturadas
process.on('uncaughtException', (err) => {
console.error('Excepción no capturada:', err);
process.exit(1);
});
process.on('unhandledRejection', (reason, promise) => {
console.error('Rechazo no manejado:', reason);
});
PM2 No Inicia al Reiniciar
# Eliminar script de inicio antiguo
pm2 unstartup
# Regenerar script de inicio
pm2 startup
# Copiar y ejecutar el comando generado
# Guardar lista de procesos de PM2
pm2 save
# Verificar procesos guardados
cat ~/.pm2/dump.pm2
# Probar reinicio
sudo reboot
Puerto Ya en Uso
# Encontrar proceso usando puerto
sudo lsof -i :3000
# Matar proceso
kill -9 $(lsof -t -i:3000)
# O cambiar puerto en configuración de ecosystem
env: {
PORT: 3001
}
# Reiniciar aplicación
pm2 reload ecosystem.config.js
Mejores Prácticas de Seguridad
Ejecutar como Usuario No-Root
# Crear usuario dedicado
sudo useradd -r -s /bin/bash pm2user
# Establecer propiedad
sudo chown -R pm2user:pm2user /var/www/my-app
# Cambiar a usuario
sudo -u pm2user pm2 start app.js
# Configurar script de inicio para ese usuario
sudo su - pm2user
pm2 startup
# Ejecutar comando generado como root
pm2 save
Variables de Entorno
# Nunca codificar secretos en duro
# Usar archivo ecosystem con variables de entorno
env_production: {
NODE_ENV: 'production',
DATABASE_URL: process.env.DATABASE_URL,
API_KEY: process.env.API_KEY
}
# O usar archivo .env (instalar dotenv)
npm install dotenv
# En app.js
require('dotenv').config();
# Iniciar con archivo env
pm2 start app.js --env production
Seguridad de Registros
# Restringir permisos de archivos de registro
chmod 640 /var/log/pm2/*.log
# Usar rotación de registros
pm2 install pm2-logrotate
pm2 set pm2-logrotate:retain 7
# No registrar datos sensibles
// En código
console.log('Usuario autenticado'); // Bueno
console.log('Contraseña:', password); // ¡Malo!
Actualizar Regularmente
# Actualizar PM2
npm update pm2 -g
# Actualizar dependencias de aplicación
npm update
npm audit fix
# Recargar aplicación
pm2 reload all
Optimización de Rendimiento
Optimización de Modo Cluster
# Instancias óptimas = núcleos de CPU
pm2 start app.js -i max
# O número específico
pm2 start app.js -i 4
# Monitorear y ajustar
pm2 monit
# Si bajo uso de CPU en instancias, reducir número
pm2 scale my-api 2
Optimización de Memoria
# Establecer límites de memoria
pm2 start app.js --max-memory-restart 500M
# Aumentar tamaño de heap de Node.js
pm2 start app.js --node-args="--max-old-space-size=4096"
# Habilitar registros de recolección de basura
pm2 start app.js --node-args="--trace-gc"
Balanceo de Carga con Nginx
upstream nodejs_backend {
least_conn;
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
server 127.0.0.1:3003;
}
server {
listen 80;
server_name api.example.com;
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_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Estrategia de Caché
// Implementar caché en aplicación
const NodeCache = require('node-cache');
const cache = new NodeCache({ stdTTL: 600 });
app.get('/api/data', async (req, res) => {
const cached = cache.get('data');
if (cached) {
return res.json(cached);
}
const data = await fetchData();
cache.set('data', data);
res.json(data);
});
Conclusión
Has configurado exitosamente PM2 para despliegue de aplicaciones Node.js en producción con estrategias integrales de gestión de procesos, monitoreo y optimización. PM2 asegura que tus aplicaciones se mantengan en línea, funcionen eficientemente y puedan gestionarse con facilidad.
Puntos Clave
- PM2 proporciona gestión de procesos de grado de producción
- El modo cluster habilita balanceo de carga entre núcleos de CPU
- Las recargas sin tiempo de inactividad mantienen las aplicaciones disponibles durante actualizaciones
- Los archivos ecosystem permiten despliegues consistentes y reproducibles
- El inicio automático asegura que las aplicaciones sobrevivan reinicios
- El monitoreo y registro integrados simplifican las operaciones
Mejores Prácticas
- Siempre usa ecosystem.config.js para producción
- Habilita scripts de inicio para recuperación automática
- Implementa rotación de registros para gestionar espacio en disco
- Usa modo cluster para utilizar todos los núcleos de CPU
- Establece límites de memoria para prevenir caídas del servidor
- Monitorea aplicaciones regularmente
- Usa recargas sin tiempo de inactividad para actualizaciones
- Mantén PM2 y Node.js actualizados
Próximos Pasos
- Configurar paneles de monitoreo (PM2 Plus, Grafana)
- Implementar despliegues automatizados (CI/CD)
- Configurar proxy inverso (Nginx/Apache)
- Configurar certificados SSL/TLS
- Implementar seguimiento integral de errores
- Crear procedimientos de recuperación ante desastres
- Configurar entornos de staging y producción
¡PM2 transforma el despliegue de aplicaciones Node.js de complejo a simple, proporcionando las herramientas necesarias para despliegues en producción confiables y escalables!
¡Feliz despliegue!


