PM2 es un gestor de procesos para Node.js que mantiene tus aplicaciones en marcha, las reinicia si fallan, reparte la carga entre todos los núcleos de la CPU en modo cluster y permite recargar el código sin cortar el servicio. En este tutorial desplegarás una aplicación Express en Ubuntu 24.04 con PM2, harás que arranque con el sistema, configurarás la rotación de logs y la publicarás detrás de Nginx con HTTPS.
Requisitos previos
Para seguir esta guía necesitas:
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath.
- Un usuario no root con privilegios
sudo. En los ejemplos se llamayour_user. - Node.js 24 LTS y npm instalados desde el repositorio de NodeSource (
node -vdebe mostrar v24). - Nginx instalado (
sudo apt install nginx). - Un dominio con un registro DNS
Aapuntando al servidor. En los ejemplos se usayour_domain.
Paso 1: Instalar PM2
Instala PM2 de forma global con npm. Con Node.js de NodeSource el directorio global pertenece a root, así que hace falta sudo:
sudo npm install -g pm2
Comprueba la versión:
pm2 --version
6.x.x
NotaSi instalaste Node.js con nvm, ejecuta
npm install -g pm2sinsudo. Ten en cuenta que en ese caso PM2 y Node.js solo existen para tu usuario.
Paso 2: Crear la aplicación de ejemplo
Si ya tienes tu aplicación en el servidor (por ejemplo, clonada con git), salta al paso 3. Si no, crea una aplicación Express mínima. Colócala en tu directorio personal para que tu usuario sea el propietario y PM2 se ejecute sin privilegios:
mkdir -p ~/apps/myapp && cd ~/apps/myapp
npm init -y
npm install express
Crea el archivo principal:
nano app.js
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
app.get('/', (req, res) => {
res.send(`Hola desde el proceso ${process.pid}\n`);
});
app.get('/health', (req, res) => {
res.json({ status: 'ok', uptime: process.uptime() });
});
const server = app.listen(port, '127.0.0.1', () => {
console.log(`Escuchando en 127.0.0.1:${port}`);
});
process.on('SIGINT', () => {
server.close(() => process.exit(0));
});
La aplicación escucha solo en 127.0.0.1 porque Nginx será quien reciba el tráfico externo. El manejador de SIGINT cierra las conexiones abiertas antes de salir: PM2 envía esa señal al detener o recargar un proceso, así que las peticiones en curso terminan correctamente.
Pruébala directamente con Node.js:
node app.js
Escuchando en 127.0.0.1:3000
Detenla con Ctrl+C.
Paso 3: Definir la aplicación en un archivo ecosystem
Puedes arrancar una aplicación con pm2 start app.js, pero un archivo ecosystem guarda toda la configuración (nombre, instancias, variables de entorno, límites) en el repositorio del proyecto y hace que cada despliegue sea reproducible.
Crea el archivo en la raíz del proyecto:
nano ecosystem.config.js
module.exports = {
apps: [
{
name: 'myapp',
script: './app.js',
cwd: __dirname,
exec_mode: 'cluster',
instances: 'max',
max_memory_restart: '300M',
kill_timeout: 5000,
env: {
NODE_ENV: 'development',
PORT: 3000,
},
env_production: {
NODE_ENV: 'production',
PORT: 3000,
},
},
],
};
Qué hace cada opción:
exec_mode: 'cluster'einstances: 'max': arranca un proceso por núcleo de CPU. Todos comparten el puerto 3000 y PM2 reparte las conexiones entre ellos. Usa un número fijo (por ejemplo2) si el servidor ejecuta otros servicios.max_memory_restart: reinicia un proceso si supera 300 MB, una red de seguridad frente a fugas de memoria.kill_timeout: milisegundos que PM2 espera a que el proceso termine trasSIGINTantes de forzarlo.env_production: variables que se aplican al arrancar con--env production.
ImportanteNo pongas secretos (contraseñas, claves de API) en este archivo si está en git. Guárdalos en un archivo
.envfuera del repositorio y cárgalos desde la aplicación, o defínelos en el entorno del servidor.
Arranca la aplicación en modo producción:
pm2 start ecosystem.config.js --env production
Comprueba su estado:
pm2 status
id name mode pid uptime ↺ status cpu mem
0 myapp cluster 41210 5s 0 online 0% 52.1mb
1 myapp cluster 41217 5s 0 online 0% 51.8mb
Todas las instancias deben estar en online. Haz varias peticiones para ver cómo las atienden procesos distintos:
for i in 1 2 3 4; do curl -s http://127.0.0.1:3000/; done
Hola desde el proceso 41210
Hola desde el proceso 41217
Hola desde el proceso 41210
Hola desde el proceso 41217
Paso 4: Arrancar PM2 con el sistema
PM2 guarda la lista de procesos en memoria. Para que las aplicaciones vuelvan a arrancar tras un reinicio del servidor, genera una unidad de systemd para tu usuario:
pm2 startup systemd
PM2 no puede crear la unidad sin privilegios, así que imprime el comando exacto que debes ejecutar:
[PM2] To setup the Startup Script, copy/paste the following command:
sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u your_user --hp /home/your_user
Copia y ejecuta esa línea tal como aparece en tu terminal. Crea y habilita el servicio pm2-your_user.
Guarda la lista de procesos actual. Es la que PM2 restaurará al arrancar:
pm2 save
[PM2] Saving current process list...
[PM2] Successfully saved in /home/your_user/.pm2/dump.pm2
Comprueba que el servicio está habilitado:
systemctl status pm2-your_user
● pm2-your_user.service - PM2 process manager
Loaded: loaded (/etc/systemd/system/pm2-your_user.service; enabled; preset: enabled)
Active: active (running) since ...
Para confirmar que todo funciona, reinicia el servidor con sudo reboot, vuelve a conectarte y ejecuta pm2 status: las instancias de myapp deben aparecer en online.
NotaEjecuta
pm2 savecada vez que añadas o elimines aplicaciones. Si no, tras el siguiente reinicio se restaurará la lista antigua.
Paso 5: Configurar la rotación de logs
PM2 guarda la salida de cada aplicación en ~/.pm2/logs/ y, por defecto, esos archivos crecen sin límite. Instala el módulo oficial de rotación:
pm2 install pm2-logrotate
Configura un tamaño máximo por archivo, cuántos archivos rotados conservar y que se compriman:
pm2 set pm2-logrotate:max_size 20M
pm2 set pm2-logrotate:retain 14
pm2 set pm2-logrotate:compress true
Consulta los logs de la aplicación cuando lo necesites:
pm2 logs myapp --lines 50
[TAILING] Tailing last 50 lines for [myapp] process (change the value with --lines option)
/home/your_user/.pm2/logs/myapp-out.log last 50 lines:
0|myapp | Escuchando en 127.0.0.1:3000
1|myapp | Escuchando en 127.0.0.1:3000
Pulsa Ctrl+C para salir. Para ver CPU y memoria de cada proceso en tiempo real, usa pm2 monit.
Paso 6: Publicar la aplicación con Nginx y HTTPS
Nginx recibirá el tráfico en los puertos 80 y 443, terminará TLS y reenviará las peticiones a la aplicación en 127.0.0.1:3000. Crea el bloque de servidor:
sudo nano /etc/nginx/sites-available/your_domain
server {
listen 80;
listen [::]:80;
server_name your_domain;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
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_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Las cabeceras Upgrade y Connection permiten WebSockets; las X-Forwarded-* le dicen a la aplicación la IP real del cliente y si la petición llegó por HTTPS. En Express, activa app.set('trust proxy', 'loopback') para que req.ip y req.protocol las usen.
Activa el sitio, abre el cortafuegos y recarga Nginx:
sudo ln -s /etc/nginx/sites-available/your_domain /etc/nginx/sites-enabled/
sudo nginx -t
sudo ufw allow 'Nginx Full'
sudo systemctl reload nginx
Obtén el certificado TLS con Certbot, que modificará el bloque para servir HTTPS y redirigir HTTP:
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d your_domain
Comprueba el acceso desde fuera del servidor:
curl https://your_domain/health
{"status":"ok","uptime":312.48}
El puerto 3000 no debe estar abierto en UFW: la aplicación solo es accesible a través de Nginx.
Paso 7: Desplegar nuevas versiones sin cortes
En modo cluster, pm2 reload reinicia las instancias de una en una y espera a que cada nueva esté lista antes de detener la siguiente, así que el servicio no deja de responder. El flujo habitual de un despliegue desde git es:
cd ~/apps/myapp
git pull
npm ci --omit=dev
pm2 reload ecosystem.config.js --env production
Comprueba que las instancias siguen en online y que el contador de reinicios (↺) solo ha subido una vez por instancia:
pm2 status
Diferencia entre los comandos de PM2 que reinician procesos:
| Comando | Efecto |
|---|---|
pm2 reload myapp | Reinicio escalonado sin cortes (modo cluster) |
pm2 restart myapp | Detiene y arranca todas las instancias a la vez; hay un corte breve |
pm2 stop myapp | Detiene la aplicación pero la mantiene en la lista |
pm2 delete myapp | La elimina de la lista de PM2 |
Si cambias variables de entorno en ecosystem.config.js, usa pm2 reload ecosystem.config.js --env production --update-env para que se apliquen, y después pm2 save.
Paso 8: Actualizar PM2
Tras actualizar el paquete global de PM2, el proceso que ya está en memoria sigue siendo el antiguo. pm2 update lo sustituye por el nuevo y restaura las aplicaciones:
sudo npm install -g pm2@latest
pm2 update
Después, vuelve a ejecutar pm2 startup systemd y el comando sudo que imprima, por si ha cambiado la ruta del binario.
Solución de problemas
La aplicación aparece como errored o el contador de reinicios no para de subir. La aplicación falla al arrancar. Consulta los errores:
pm2 logs myapp --err --lines 100
Un EADDRINUSE indica que otro proceso ocupa el puerto. Averigua cuál con sudo ss -ltnp 'sport = :3000'.
Nginx devuelve 502 Bad Gateway. La aplicación no está escuchando en la dirección de proxy_pass. Comprueba pm2 status y que curl http://127.0.0.1:3000/health responde desde el servidor. El log /var/log/nginx/error.log mostrará connect() failed (111: Connection refused).
Las aplicaciones no arrancan tras reiniciar el servidor. Falta pm2 save o la unidad de systemd no está habilitada. Revisa el servicio:
systemctl status pm2-your_user
sudo journalctl -u pm2-your_user -n 50 --no-pager
Si el servicio no existe, repite el paso 4.
Los procesos se reinician solos cada cierto tiempo. Probablemente superan max_memory_restart. pm2 show myapp indica el número de reinicios y pm2 monit la memoria en tiempo real. Sube el límite si el consumo es estable o busca la fuga si crece sin parar.
Conclusión
Tu aplicación Node.js se ejecuta ahora con PM2 en modo cluster en Ubuntu 24.04, arranca con el sistema mediante systemd, rota sus logs y se publica por HTTPS a través de Nginx. Las nuevas versiones se despliegan con git pull, npm ci y pm2 reload sin cortar el servicio.
Como siguientes pasos, automatiza ese despliegue desde tu sistema de CI, añade una comprobación externa del endpoint /health en tu herramienta de monitorización o configura límites de peticiones en Nginx para proteger la aplicación.
