Despliegue NestJS sin downtime con PM2 y GitLab

Desplegar un backend de NestJS en producción sin que se caiga ni una request es más difícil de lo que prometen la mayoría de los tutoriales. La clave está en usar PM2 en modo cluster con pm2 reload (no restart), un reverse proxy con Nginx y un pipeline de GitLab CI/CD que haga el deploy sin cortar el servicio. Así lo documenta una guía publicada el 3 de julio de 2026 sobre un stack real en producción.

El despliegue NestJS sin downtime es la técnica de actualizar una aplicación NestJS en un servidor de producción reemplazando los procesos de a uno, de modo que Nginx siempre tenga al menos un worker vivo para atender el tráfico. Se apoya en el modo cluster de PM2, que levanta varias instancias de Node.js y las recarga en secuencia con pm2 reload, sin descartar conexiones activas.

En 30 segundos

  • El comando que importa: pm2 reload recarga sin cortar tráfico, pm2 restart mata y levanta (hay downtime).
  • Stack de la guía: Droplet con Ubuntu 24.04, Node.js v18.20.8 instalado desde el binario oficial, PM2, Nginx y GitLab CI/CD.
  • Node desde binario, no desde apt: los scripts de setup a veces instalan v20 aunque pidas otra versión.
  • Nunca deployes como root: creá un usuario deployer dedicado con SSH keys y permisos acotados.
  • El pipeline hace todo: git pull, npm ci, npm run build y pm2 reload en el stage de deploy.

¿Por qué el downtime en NestJS te cuesta plata (y ranking)?

Ponele que tenés una API de checkout y hacés deploy un jueves a la tarde. Si el proceso se reinicia en seco, cada request que estaba en vuelo se corta con un 502. El usuario ve un error, Google ve un servidor que responde mal justo cuando pasa el bot, y vos ves el ticket de soporte al otro día.

Acá está la diferencia que casi nadie explica bien. Un restart tira abajo todos los procesos y los vuelve a levantar: hay una ventana, corta pero real, donde nada atiende. Un reload en modo cluster reemplaza los workers de a uno, esperando que el nuevo esté listo antes de bajar el viejo. El tráfico nunca se queda sin nadie que lo atienda. En nuestro análisis de pipelines CI/CD profundizamos sobre esto.

¿Vale la pena el laburo extra de configurarlo? Si tu app factura o la usan clientes reales, sí. Si es un side project que ves vos solo, quizás un restart de dos segundos zafa.

¿Qué infraestructura necesitás para el despliegue NestJS sin downtime?

El stack de la guía es concreto y cada pieza está por una razón. Nada de over-engineering.

  • Servidor: un Droplet con Ubuntu 24.04. Cualquier VPS con Ubuntu sirve. Si buscás hosting o un VPS en Argentina con soporte en español, donweb.com tiene opciones que evitan la latencia de un servidor afuera.
  • Runtime: Node.js v18.20.8, instalado desde el tarball oficial de nodejs.org. La guía insiste en esto porque los scripts de instalación tipo NodeSource a veces te clavan v20 aunque pidas la 18.
  • Gestor de procesos: PM2, que maneja el modo cluster y las recargas. Es la pieza central de todo esto.
  • Reverse proxy: Nginx adelante, que recibe el tráfico HTTPS y lo manda a los procesos de Node por el puerto interno.
  • CI/CD: GitLab, para que el deploy sea un git push y no una sesión de SSH manual a las 11 de la noche.

Un detalle de seguridad que la guía marca como no negociable: el proceso corre bajo un usuario deployer no-root. Se crea el directorio en /var/www/nestapp y los logs en /var/log/pm2, todo con ownership de ese usuario. Deployar como root es pedir que el día que algo se rompa, se rompa en grande. Para más detalles técnicos, mirá alternativas de orquestación para CI/CD.

¿Cómo configurar PM2 en modo cluster?

Primero instalás PM2 de forma global con npm install -g pm2. Después definís todo en un archivo ecosystem.config.js en la raíz del proyecto. El parámetro que activa la magia es instances: "max", que le dice a PM2 que levante tantos workers como núcleos tenga el servidor.

module.exports = {
 apps: [{
 name: "nestapp",
 script: "dist/main.js",
 instances: "max",
 exec_mode: "cluster",
 env: {
 NODE_ENV: "production",
 PORT: 3000
 },
 error_file: "/var/log/pm2/nestapp-error.log",
 out_file: "/var/log/pm2/nestapp-out.log"
 }]
}

Con exec_mode: "cluster", Node usa su módulo de cluster nativo para balancear las conexiones entre workers. Y acá está el punto que separa un deploy prolijo de uno que corta: la diferencia entre los dos comandos de PM2.

ComandoQué haceDowntimeCuándo usarlo
pm2 restartMata todos los procesos y los levanta de ceroSí, breveCambios en env vars o config que exige arranque limpio
pm2 reloadReemplaza workers de a uno, esperando al nuevoNoDeploys de código en producción
pm2 killFrena el daemon de PM2 enteroSí, totalReset completo o troubleshooting
despliegue nestjs sin downtime diagrama explicativo

Para producción, reload. Siempre. Lo documenta la propia doc de PM2 sobre cluster mode: la recarga es la que te da el zero-downtime.

¿Cómo configurar Nginx como reverse proxy?

Nginx se para adelante y hace de portero. Recibe el tráfico en el 443 con SSL y lo reenvía al puerto donde escuchan tus procesos de Node. La config básica va en /etc/nginx/sites-available/. Sobre eso hablamos en consideraciones de seguridad en deployments.

server {
 listen 80;
 server_name tuapi.com;

 location / {
 proxy_pass http://localhost:3000;
 proxy_http_version 1.1;
 proxy_set_header Host $host;
 proxy_set_header X-Real-IP $remote_addr;
 }
}

Para el SSL, lo típico es Certbot con Let’s Encrypt, que te configura el bloque 443 solo. Cuando recargás Nginx tras un cambio, usá nginx -s reload o systemctl reload nginx. Ese reload también es graceful: Nginx aplica la config nueva sin cortar las conexiones abiertas. La guía de PM2 con Nginx cubre el setup completo.

¿Cómo automatizar el deploy con GitLab CI/CD?

La idea es que hacés push a la rama de producción y el pipeline se encarga del resto. Se define en .gitlab-ci.yml con tres etapas: build, test y deploy.

stages:
 - build
 - test
 - deploy

build:
 stage: build
 script:
 - npm ci
 - npm run build

deploy:
 stage: deploy
 only:
 - main
 script:
 - ssh deployer@$SERVER_IP "cd /var/www/nestapp &&
 git pull &&
 npm ci &&
 npm run build &&
 pm2 reload ecosystem.config.js"

El paso previo es generar una SSH key para el runner de GitLab y cargar la clave pública en el authorized_keys del usuario deployer. La clave privada va como variable protegida en GitLab CI/CD, nunca en el repo. El npm ci (en vez de npm install) te garantiza que instala exacto lo que dice el lockfile, sin sorpresas de versiones. Un enfoque parecido con GitLab lo describe este walkthrough de continuous delivery.

El corazón del deploy sin downtime es esa última línea: pm2 reload. Todo lo anterior arma el build nuevo; el reload lo pone en producción sin que se caiga nadie.

Seguridad: lo que no podés saltear

  • Usuario no-root para la app: el proceso corre como deployer. Si alguien compromete la app, no tiene la máquina entera.
  • Permisos de SSH exactos: chmod 700 en ~/.ssh y chmod 600 en authorized_keys. Con permisos más laxos, SSH directamente ignora las keys.
  • Nunca commitees el .env: los secretos van en las variables de GitLab CI/CD, protegidas y enmascaradas. Un .env en el repo es la fuga de credenciales más común que existe.
  • Firewall activo: abrí solo 22, 80 y 443. El puerto 3000 de Node no debería ser accesible desde afuera, solo lo toca Nginx en localhost.

¿Cómo monitorear y debuggear después del deploy?

PM2 trae las herramientas para ver qué está pasando. pm2 status te lista los procesos con su estado, CPU y memoria. pm2 logs te tira los logs en vivo, y pm2 monit abre un dashboard en la terminal con métricas por worker. Lo explicamos a fondo en plataformas de control de versiones disponibles.

Si algo salió mal, primero mirá los archivos en /var/log/pm2. Los errores más comunes son bastante predecibles: variables de entorno faltantes (la app arranca y muere al toque), una SSH key mal cargada que hace fallar el stage de deploy, o un puerto ya ocupado. Cuando nada de eso alcanza y el proceso quedó en un estado raro, pm2 kill seguido de un arranque limpio del ecosystem suele destrabar. Guía oficial de referencia: la doc de deployment de NestJS.

Errores comunes que te arruinan el zero-downtime

  • Usar restart en el pipeline: es el error número uno. Configurás todo el cluster y después el CI hace pm2 restart, que corta el tráfico igual. Cambialo por reload.
  • Instalar Node desde apt o NodeSource sin fijar versión: terminás con v20 cuando tu app se testeó en v18, y el bug aparece recién en producción. Instalá desde el binario oficial.
  • Correr npm install en el deploy: puede resolver versiones distintas a las de tu lockfile. Usá npm ci, que es determinístico.
  • Poner instances: 1 y creer que tenés cluster: con una sola instancia no hay recarga rolling posible. Sin varios workers, no hay zero-downtime.

Preguntas Frecuentes

¿Cuál es la diferencia entre pm2 restart y pm2 reload?

pm2 reload reemplaza los workers de a uno esperando que el nuevo esté listo, así que no hay corte de servicio. pm2 restart mata todos los procesos y los vuelve a levantar, lo que genera una ventana breve de downtime. Para deploys de producción se usa reload.

¿Por qué instalar Node.js desde el binario y no desde apt?

Porque los scripts de instalación tipo NodeSource a veces instalan v20 aunque especifiques otra versión. Bajar el tarball oficial de nodejs.org, en este caso Node v18.20.8, te garantiza la versión exacta con la que testeaste la app. Así evitás bugs que aparecen solo en producción por diferencias de runtime.

¿Necesito Nginx si ya uso PM2?

Sí, cumplen roles distintos. PM2 gestiona los procesos de Node y balancea entre workers, mientras que Nginx maneja el HTTPS, el reverse proxy y las conexiones externas. Exponer Node directo a internet sin un reverse proxy adelante no es recomendable en producción.

¿Cómo guardo los secretos en GitLab CI/CD?

Como variables protegidas y enmascaradas en la sección CI/CD del proyecto en GitLab. La SSH key privada del deployer y las credenciales de la app van ahí, nunca en el .env ni en el repositorio. El pipeline las inyecta en tiempo de ejecución.

¿Puedo hacer zero-downtime con una sola instancia?

No de forma limpia. El zero-downtime en PM2 depende del modo cluster con varios workers, porque la recarga reemplaza uno mientras los otros siguen atendiendo. Con instances: 1 no hay proceso de respaldo durante la recarga, así que habría un corte.

Conclusión

El despliegue NestJS sin downtime no es magia, es una combinación bien puesta: PM2 en modo cluster con reload, Nginx adelante y un pipeline de GitLab que reemplaza el build sin cortar tráfico. La mayoría de los cortes en producción vienen de tres errores evitables: usar restart en vez de reload, instalar la versión de Node equivocada y deployar como root.

Si estás por armar esto, arrancá por el ecosystem.config.js con instances: "max" y probá un pm2 reload a mano antes de meterlo en el CI. Cuando lo veas funcionar sin tirar un solo 502, recién ahí automatizalo con GitLab. Un buen VPS con soporte cerca y la config correcta te ahorran el ticket de soporte del jueves a la noche.

Fuentes

Te puede interesar...