¿Por qué desplegar Node.js en un VPS en lugar de en una plataforma gestionada?
Las plataformas serverless y PaaS resultan cómodas al principio, pero su coste crece con el tráfico e imponen restricciones (timeouts, cold starts, límites de conexiones WebSocket). Un VPS le ofrece un proceso Node.js persistente, conexiones largas (WebSocket, SSE) sin límites artificiales, recursos dedicados y un coste fijo y previsible. Usted decide la versión de Node, el gestor de procesos, el proxy inverso y la estrategia de actualización, sin depender de las convenciones de un proveedor.
El modelo asíncrono y no bloqueante de Node.js permite que un único proceso mantenga un gran número de conexiones simultáneas: por eso es un buen candidato para API REST, funciones en tiempo real y microservicios, siempre que ese proceso siga vivo. Es justo lo que rechazan la mayoría de los alojamientos compartidos, que prohíben los procesos persistentes o los WebSocket. En un VPS, sus dependencias npm, sus variables de entorno y su configuración de PM2 no se comparten con ninguna otra cuenta.
Lo que gana con un VPS para Node.js
- Proceso Node.js persistente, ideal para WebSocket, SSE y tareas largas sin cold start
- Reinicio automático tras un fallo y al reiniciar el servidor gracias a PM2
- Modo cluster para aprovechar todos los núcleos de CPU del VPS sin cambiar su código
- Proxy inverso Nginx delante de Node para HTTPS, compresión y archivos estáticos
- Workers de colas (BullMQ, Agenda) y tareas programadas que se ejecutan junto a la API, en la misma máquina
- Varias aplicaciones en un solo VPS, cada una en su puerto interno (3000, 3001…) y su subdominio detrás de Nginx
- Renderizado en servidor de frameworks como Nuxt o Next.js, servidos por la misma base de Node.js y PM2
- Coste fijo y previsible, sin facturación por petición ni sorpresas al escalar
- Control total de la versión de Node.js y de las dependencias del sistema
Requisitos previos
Una aplicación Node.js estándar (API Express, Nest, Fastify) funciona con holgura en un VPS de 1 a 2 vCPU y de 1 a 2 GB de RAM; prevea más RAM si compila un front pesado durante el build o ejecuta workers. Apunte a 2 vCPU y 2 GB en cuanto la API comparta la máquina con un renderizado SSR o una base de datos PostgreSQL.
Elija una línea LTS que siga mantenida: Node.js 20 ya no recibe correcciones desde el 30 de abril de 2026, Node.js 24 pasó a mantenimiento el 20 de octubre de 2026 y Node.js 26 es la LTS activa desde el 28 de octubre de 2026. Instálela mediante nvm o el repositorio de NodeSource para fijarla, junto con PM2 como gestor de procesos y Nginx como proxy inverso. Necesita un dominio que apunte a la IP del VPS y los puertos 80/443 abiertos.
Partir de la plantilla Node.js Stack
La plantilla Node.js Stack del Marketplace de ServOrbit prepara un VPS Ubuntu 24.04 con Node.js en la línea LTS actual del repositorio de NodeSource, npm, PM2 instalado de forma global, Nginx y PostgreSQL. Le ahorra el primer paso de la lista siguiente; el resto de la guía se aplica tal cual.
No es una aplicación con pantalla de inicio de sesión, sino un entorno de ejecución: se accede por SSH (ssh root@<ip-del-vps>) para subir el código. En el primer acceso, Nginx sirve su página de bienvenida por defecto en el puerto 80, porque aún no hay ningún virtual host escrito para su dominio, y Certbot no está instalado: los pasos de Nginx y HTTPS siguen pendientes. PostgreSQL solo escucha en local y autentica al usuario del sistema postgres con el método peer: sudo -u postgres psql abre la consola sin contraseña.
Desplegar una aplicación Node.js en producción
Preparar el VPS e instalar Node.js
En un VPS Ubuntu 24.04, actualice el sistema e instale una versión LTS de Node mediante nvm (
nvm install --lts) o el repositorio de NodeSource. Compruebe connode -vynpm -v. Cree un usuario no root dedicado a la aplicación en lugar de ejecutarlo todo como root: ese usuario será el propietario del código, del archivo.envy de los procesos de PM2. Con la plantilla Node.js Stack, Node.js, npm y PM2 ya están instalados: solo queda crear el usuario.Obtener el código y hacer el build
Clone su repositorio en el VPS, instale las dependencias de producción (
npm ci --omit=dev) y lance el build si hace falta (npm run build).Guarde las variables sensibles en un archivo
.envno versionado, legible solo por el usuario de la aplicación (chmod 600 .env). No necesita ninguna librería para leerlo: desde Node.js 20.6, la opción--env-filecarga ese archivo enprocess.enval arrancar.Preparar la base de datos PostgreSQL
Si la aplicación necesita una base de datos, cree un rol y una base dedicados en lugar de trabajar con el superusuario:
sudo -u postgres createuser --pwprompt monapp sudo -u postgres createdb --owner=monapp monappDespués indique
DATABASE_URL=postgres://monapp:<contraseña>@127.0.0.1:5432/monappen el.env. Como PostgreSQL solo escucha en local, no hay que abrir ningún puerto de base de datos en el cortafuegos.Lanzar la aplicación con PM2
Instale PM2 de forma global (
npm install -g pm2, ya hecho en la plantilla) y arranque la aplicación desde la carpeta del proyecto, pidiendo a Node que cargue el.env:pm2 start app.js --name monapp -i max --node-args="--env-file=.env"El flag
-i maxactiva el modo cluster en todos los núcleos. PM2 reinicia automáticamente el proceso si falla.Activar el arranque automático al iniciar
Genere el script de systemd con
pm2 startup, ejecutado por el usuario de la aplicación, lance con sudo el comando que se muestra y después fije el estado actual conpm2 save. En el próximo reinicio del VPS, su aplicación se relanzará automáticamente, sin intervención.Configurar Nginx como proxy inverso
Instale Nginx si aún no está presente y cree
/etc/nginx/sites-available/monapp, un virtual host que redirige al puerto local de Node. Las cabecerasUpgradeyConnectionsirven para los WebSocket, yX-Forwarded-Protoindica a la aplicación que el cliente está en HTTPS:server { listen 80; server_name yourdomain.com; location / { proxy_pass http://127.0.0.1:3000; 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-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }Active el sitio, retire la página por defecto, pruebe la configuración y recargue:
ln -s /etc/nginx/sites-available/monapp /etc/nginx/sites-enabled/ rm /etc/nginx/sites-enabled/default nginx -t && systemctl reload nginxProteger con HTTPS mediante Let's Encrypt
Instale Certbot y su plugin para Nginx y solicite el certificado:
apt install certbot python3-certbot-nginx certbot --nginx -d yourdomain.comCertbot obtiene el certificado, configura la redirección HTTP→HTTPS y programa la renovación automática. Su aplicación Node ya se sirve en HTTPS.
Centralizar la configuración en ecosystem.config.js
En cuanto el VPS aloja más de una aplicación, o una API va acompañada de un worker, las opciones pasadas por línea de comandos se vuelven difíciles de seguir. PM2 lee un archivo ecosystem.config.js que describe cada proceso: script, carpeta, número de instancias, límite de memoria y variables no sensibles.
module.exports = {
apps: [
{
name: 'api',
cwd: '/home/monapp/app',
script: 'app.js',
instances: 'max',
exec_mode: 'cluster',
node_args: '--env-file=.env',
max_memory_restart: '500M',
env: { NODE_ENV: 'production', PORT: 3000 }
},
{
name: 'worker',
cwd: '/home/monapp/app',
script: 'worker.js',
node_args: '--env-file=.env'
}
]
}pm2 start ecosystem.config.js arranca el conjunto, pm2 reload ecosystem.config.js lo actualiza y la opción --only api apunta a una sola aplicación. Aquí la API funciona en modo cluster en todos los núcleos y el worker en un único proceso. Versione este archivo junto con el código; los secretos, en cambio, se quedan en el .env, fuera del repositorio.
Registros: leerlos y después rotarlos
PM2 captura la salida estándar y la salida de errores de cada proceso en ~/.pm2/logs/, con un archivo -out.log y otro -error.log por aplicación. pm2 logs monapp los sigue en directo y pm2 logs monapp --lines 100 muestra las últimas líneas.
Sin rotación, estos archivos crecen hasta llenar el disco. El módulo pm2-logrotate se encarga de ello; por defecto rota un archivo al superar los 10 MB y conserva 30, umbrales que se ajustan con facilidad:
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 50M
pm2 set pm2-logrotate:retain 7En el lado del proxy, un error 502 aparece en /var/log/nginx/error.log: casi siempre significa que el proceso Node.js está detenido o escucha en un puerto distinto del indicado en proxy_pass.
Para despliegues sin interrupción, combine el modo cluster de PM2 con pm2 reload monapp en lugar de restart: PM2 reinicia los workers uno a uno y mantiene siempre al menos una instancia disponible durante la actualización. Vigile después la memoria con pm2 monit y fije un límite de reinicio automático (--max-memory-restart 500M) para prevenir las fugas de memoria en producción.
La documentación oficial
Para las opciones propias de cada herramienta y los cambios de versión, consulte la documentación oficial de Node.js, el calendario de versiones LTS de Node.js y la documentación de PM2. Esta guía cubre la puesta en producción en un VPS; la documentación del editor sigue siendo la referencia para los ajustes finos.