Cómo desplegar una aplicación Node.js en una VPS con Nginx

Desarrollar una aplicación Node.js en nuestro ordenador es relativamente sencillo. El siguiente paso es publicarla en Internet para que pueda ser utilizada desde un dominio y permanezca disponible las 24 horas.

En esta guía aprenderás a desplegar una aplicación Node.js en una VPS con Ubuntu o Debian, mantenerla funcionando mediante PM2 y utilizar Nginx como reverse proxy.

Al finalizar tendremos una arquitectura similar a esta:

Internet
   │
   ▼
https://app.midominio.com
   │
   ▼
Nginx :443
   │
   ▼
127.0.0.1:3000
   │
   ▼
Aplicación Node.js
   │
   ▼
PM2

Nuestra aplicación Node.js no estará expuesta directamente a Internet. Nginx recibirá las conexiones HTTP/HTTPS y las enviará internamente hacia Node.js.

También configuraremos un certificado SSL gratuito para acceder a nuestra aplicación mediante HTTPS.


¿Qué necesitamos?

Para seguir esta guía necesitaremos:

  • Una VPS con Ubuntu o Debian.
  • Acceso mediante SSH.
  • Usuario con permisos sudo.
  • Una aplicación Node.js.
  • Un dominio o subdominio.
  • Acceso a la configuración DNS del dominio.

Para las pruebas utilizaremos:

app.midominio.com

y nuestra aplicación escuchará internamente en:

127.0.0.1:3000

Debes sustituir app.midominio.com por tu propio dominio.


1. Conectarnos a la VPS mediante SSH

Desde Linux, macOS o Windows podemos conectarnos utilizando:

ssh usuario@IP_DEL_SERVIDOR

Por ejemplo:

ssh root@203.0.113.10

O si utilizamos un usuario diferente:

ssh usuario@203.0.113.10

Una vez dentro podemos comenzar a preparar el servidor.


2. Actualizar Ubuntu o Debian

Antes de instalar los paquetes actualizamos la información de los repositorios:

sudo apt update

También podemos actualizar los paquetes instalados:

sudo apt upgrade -y

3. Instalar las herramientas necesarias

Instalamos curl, Git y algunas utilidades básicas:

sudo apt install curl git ca-certificates -y

Comprobamos Git:

git --version

4. Instalar Node.js

Para un servidor en producción es recomendable utilizar una versión LTS de Node.js.

Vamos a utilizar los paquetes distribuidos mediante NodeSource para Ubuntu y Debian.

Descargamos el instalador de la rama LTS:

curl -fsSL https://deb.nodesource.com/setup_lts.x -o nodesource_setup.sh

Ejecutamos el script:

sudo -E bash nodesource_setup.sh

Ahora instalamos Node.js:

sudo apt install nodejs -y

Con este paquete también tendremos disponible npm.


5. Comprobar Node.js y npm

Consultamos la versión instalada:

node -v

Y comprobamos npm:

npm -v

Si ambos comandos muestran una versión, Node.js está correctamente instalado.

Para aplicaciones en producción conviene utilizar versiones LTS en lugar de versiones experimentales o que hayan alcanzado su fin de soporte.


6. Subir nuestra aplicación Node.js

Existen diferentes formas de subir una aplicación a nuestra VPS.

Podemos utilizar:

  • Git.
  • GitHub.
  • GitLab.
  • SFTP.
  • SCP.
  • rsync.
  • Un sistema de despliegue automatizado.

En este ejemplo utilizaremos Git.

Creamos el directorio donde almacenaremos nuestras aplicaciones:

sudo mkdir -p /var/www

Asignamos el directorio a nuestro usuario:

sudo chown -R $USER:$USER /var/www

Entramos:

cd /var/www

Ahora clonamos nuestro proyecto:

git clone https://github.com/usuario/mi-aplicacion.git

Entramos en el proyecto:

cd mi-aplicacion

7. Instalar las dependencias

Si nuestro proyecto contiene:

package-lock.json

es recomendable utilizar:

npm ci

npm ci instala exactamente las versiones definidas en el archivo de bloqueo.

Si nuestro proyecto no dispone de package-lock.json, podemos ejecutar:

npm install

8. Crear una aplicación Node.js de prueba

Si todavía no tienes una aplicación preparada, podemos crear una sencilla para comprobar todo el despliegue.

Creamos un directorio:

mkdir -p /var/www/node-demo

Entramos:

cd /var/www/node-demo

Inicializamos el proyecto:

npm init -y

Instalamos Express:

npm install express

Creamos:

nano app.js

Y añadimos:

const express = require('express');

const app = express();

const PORT = process.env.PORT || 3000;
const HOST = '127.0.0.1';

app.get('/', (req, res) => {
    res.send(`
        <h1>¡Node.js está funcionando!</h1>
        <p>Aplicación desplegada correctamente en nuestra VPS.</p>
    `);
});

app.listen(PORT, HOST, () => {
    console.log(`Aplicación ejecutándose en http://${HOST}:${PORT}`);
});

Guardamos el archivo.

Ahora podemos ejecutarlo:

node app.js

Deberíamos obtener:

Aplicación ejecutándose en http://127.0.0.1:3000

9. Comprobar la aplicación

Sin cerrar la aplicación podemos abrir otra conexión SSH y ejecutar:

curl http://127.0.0.1:3000

Deberíamos recibir el HTML de nuestra aplicación.

También podemos comprobar qué proceso utiliza el puerto:

sudo ss -tulpn | grep :3000

Lo ideal en esta configuración es encontrar:

127.0.0.1:3000

en lugar de:

0.0.0.0:3000

Esto significa que nuestra aplicación solamente acepta conexiones desde el propio servidor.

Nginx será quien posteriormente se encargue de aceptar conexiones públicas.


10. ¿Por qué no ejecutar simplemente node app.js?

Si iniciamos nuestra aplicación mediante:

node app.js

funcionará mientras nuestra terminal permanezca abierta.

Pero si:

  • Cerramos SSH.
  • La aplicación falla.
  • El proceso termina.
  • Reiniciamos la VPS.

nuestra aplicación dejará de estar disponible.

Para evitarlo utilizaremos un gestor de procesos.


11. Instalar PM2

PM2 es un administrador de procesos especialmente popular para aplicaciones Node.js.

Lo instalamos globalmente mediante npm:

sudo npm install -g pm2

Comprobamos la instalación:

pm2 --version

12. Ejecutar nuestra aplicación con PM2

Si nuestra aplicación se inicia mediante:

node app.js

podemos utilizar:

pm2 start app.js --name mi-app

PM2 iniciará Node.js en segundo plano.

Consultamos las aplicaciones:

pm2 list

Deberíamos encontrar:

mi-app

con estado:

online

13. Ejecutar un proyecto mediante npm

Muchas aplicaciones tienen definido un comando:

npm start

En ese caso podemos ejecutar:

pm2 start npm --name mi-app -- start

Por ejemplo, si nuestro package.json contiene:

{
  "scripts": {
    "start": "node app.js"
  }
}

PM2 ejecutará automáticamente:

npm start

14. Consultar los logs de la aplicación

Podemos ver los logs mediante:

pm2 logs mi-app

Para mostrar únicamente las últimas líneas:

pm2 logs mi-app --lines 100

También podemos consultar todos los procesos:

pm2 list

Y obtener información detallada:

pm2 show mi-app

15. Reiniciar nuestra aplicación

Después de realizar cambios:

pm2 restart mi-app

Para detenerla:

pm2 stop mi-app

Y volver a iniciarla:

pm2 start mi-app

También podemos eliminarla completamente de PM2:

pm2 delete mi-app

16. Hacer que Node.js arranque después de reiniciar la VPS

Ahora tenemos un problema.

Si reiniciamos completamente el servidor, necesitamos que PM2 vuelva a iniciar nuestras aplicaciones.

Primero ejecutamos:

pm2 startup

PM2 detectará automáticamente systemd y mostrará un comando que debemos ejecutar con sudo.

Será parecido a:

sudo env PATH=$PATH:... pm2 startup systemd -u usuario --hp /home/usuario

Debemos copiar y ejecutar exactamente el comando que PM2 muestre en nuestro servidor.

Después guardamos la lista actual de procesos:

pm2 save

Ahora PM2 podrá recuperar nuestras aplicaciones después de reiniciar el servidor.

Podemos comprobarlo reiniciando:

sudo reboot

Después de volver a conectarnos:

pm2 list

Nuestra aplicación debería aparecer nuevamente como:

online

17. Instalar Nginx

Ahora vamos a colocar Nginx delante de nuestra aplicación.

Instalamos:

sudo apt install nginx -y

Comprobamos su estado:

sudo systemctl status nginx

Debería aparecer:

active (running)

También podemos hacer que se inicie automáticamente:

sudo systemctl enable nginx

18. Configurar el DNS del dominio

Antes de configurar HTTPS necesitamos que nuestro dominio apunte hacia la VPS.

En nuestro proveedor DNS creamos un registro:

Tipo: A
Nombre: app
Destino: IP_DE_LA_VPS

Por ejemplo:

app.midominio.com → 203.0.113.10

Podemos comprobar la resolución utilizando:

ping app.midominio.com

O:

dig app.midominio.com

También podemos utilizar:

nslookup app.midominio.com

La dirección devuelta debe coincidir con la IP pública de nuestra VPS.


19. Configurar Nginx como reverse proxy

Ahora creamos una configuración específica para nuestra aplicación:

sudo nano /etc/nginx/sites-available/mi-app

Añadimos:

server {
    listen 80;
    listen [::]:80;

    server_name app.midominio.com;

    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";
    }
}

Debemos sustituir:

app.midominio.com

por nuestro dominio real.

Y:

127.0.0.1:3000

por el puerto utilizado por nuestra aplicación.


20. ¿Qué hace proxy_pass?

La parte más importante de nuestra configuración es:

proxy_pass http://127.0.0.1:3000;

Cuando alguien accede a:

https://app.midominio.com

Nginx recibe la solicitud.

Después la envía internamente hacia:

http://127.0.0.1:3000

Nuestra aplicación Node.js procesa la petición y devuelve la respuesta a Nginx.

Finalmente Nginx envía esa respuesta al usuario.

De esta manera el visitante nunca necesita acceder directamente al puerto 3000.


21. Habilitar nuestra configuración de Nginx

Creamos un enlace simbólico:

sudo ln -s /etc/nginx/sites-available/mi-app /etc/nginx/sites-enabled/

Antes de aplicar los cambios comprobamos siempre la sintaxis:

sudo nginx -t

Si todo está correcto veremos algo parecido a:

syntax is ok
test is successful

Aplicamos los cambios:

sudo systemctl reload nginx

Ahora podemos acceder a:

http://app.midominio.com

Nuestra aplicación Node.js debería aparecer en el navegador.


22. ¿Por qué utilizar Nginx delante de Node.js?

Podríamos hacer que Node.js escuchara directamente mediante:

0.0.0.0:3000

y acceder utilizando:

http://IP:3000

Pero para un entorno de producción normalmente resulta mucho más práctico colocar un reverse proxy delante.

Nginx puede encargarse de:

  • HTTP.
  • HTTPS.
  • Certificados SSL.
  • Dominios y subdominios.
  • Redirecciones.
  • Compresión.
  • Caché.
  • Cabeceras.
  • Rate limiting.
  • Archivos estáticos.
  • Múltiples aplicaciones dentro de una misma VPS.

Nuestra aplicación Node.js puede mantenerse escuchando únicamente de forma local.


23. Comprobar que el puerto 3000 no está expuesto

Ejecutamos:

sudo ss -tulpn | grep :3000

Si nuestra aplicación está configurada como en el ejemplo deberíamos ver:

127.0.0.1:3000

Esto significa que únicamente el propio servidor puede conectarse directamente al puerto.

Desde Internet los usuarios accederán utilizando:

80
443

a través de Nginx.


24. Configurar el firewall

Si nuestro servidor utiliza UFW podemos permitir SSH:

sudo ufw allow OpenSSH

Y permitir Nginx:

sudo ufw allow 'Nginx Full'

Consultamos las reglas:

sudo ufw status

Los puertos web necesarios serán:

80/TCP
443/TCP

No necesitamos abrir:

3000/TCP

porque nuestra aplicación solamente debe ser accesible internamente mediante Nginx.

Antes de activar un firewall mediante SSH, comprueba siempre que el puerto SSH esté permitido para evitar perder el acceso al servidor.


25. Añadir HTTPS a nuestra aplicación

Una vez que:

http://app.midominio.com

funcione correctamente podemos configurar HTTPS.

Necesitaremos un certificado SSL.

Una opción muy utilizada es Let's Encrypt mediante Certbot.

En Ubuntu y Debian podemos instalar los paquetes disponibles para Nginx:

sudo apt update
sudo apt install certbot python3-certbot-nginx -y

Después solicitamos el certificado:

sudo certbot --nginx -d app.midominio.com

Certbot comprobará el dominio y podrá modificar nuestra configuración de Nginx para habilitar HTTPS.


26. Comprobar HTTPS

Después de terminar podemos abrir:

https://app.midominio.com

Nuestra aplicación debería cargarse mediante una conexión HTTPS.

También podemos comprobar Nginx:

sudo nginx -t

Y recargarlo:

sudo systemctl reload nginx

27. Comprobar la renovación automática del certificado

Los certificados de Let's Encrypt deben renovarse periódicamente.

Podemos comprobar que el proceso de renovación funciona mediante:

sudo certbot renew --dry-run

Si la prueba termina correctamente, nuestro sistema está preparado para renovar el certificado cuando sea necesario.


28. Aplicaciones Node.js con WebSockets

Algunas aplicaciones Node.js utilizan WebSockets.

Por ejemplo:

  • Chats.
  • Socket.IO.
  • Notificaciones en tiempo real.
  • Dashboards.
  • Juegos.
  • Sistemas de monitorización.

En nuestro reverse proxy hemos incluido:

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

Estas cabeceras permiten que Nginx gestione correctamente el cambio de protocolo necesario para determinadas conexiones WebSocket.

Una configuración típica sería:

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";
}

29. Configurar variables de entorno

Las aplicaciones reales suelen necesitar información como:

DATABASE_URL
JWT_SECRET
API_KEY
PORT
NODE_ENV

Nunca debemos almacenar contraseñas o secretos directamente en un repositorio Git público.

Podemos utilizar variables de entorno o un archivo:

.env

Por ejemplo:

NODE_ENV=production
PORT=3000
DATABASE_URL=mysql://usuario:password@127.0.0.1/database

Es muy importante añadir .env al:

.gitignore

Por ejemplo:

node_modules/
.env

para evitar subir nuestras credenciales accidentalmente al repositorio.


30. Configurar NODE_ENV en producción

Muchas aplicaciones Node.js modifican su comportamiento dependiendo del entorno.

Podemos definir:

NODE_ENV=production

Si utilizamos PM2 podemos crear un archivo:

nano ecosystem.config.js

Por ejemplo:

module.exports = {
    apps: [
        {
            name: 'mi-app',
            script: './app.js',
            env: {
                NODE_ENV: 'production',
                PORT: 3000
            }
        }
    ]
};

Ahora podemos iniciar nuestra aplicación:

pm2 start ecosystem.config.js

Guardamos:

pm2 save

31. Actualizar nuestra aplicación desde Git

Una vez que la aplicación está publicada seguramente necesitaremos desplegar cambios.

Entramos en el proyecto:

cd /var/www/mi-aplicacion

Descargamos los cambios:

git pull

Instalamos las dependencias exactamente como están definidas:

npm ci

Y reiniciamos:

pm2 restart mi-app

Si hemos modificado variables de entorno:

pm2 restart mi-app --update-env

Podemos comprobar:

pm2 list

Y revisar los logs:

pm2 logs mi-app

32. Flujo habitual para actualizar una aplicación Node.js

En muchos proyectos el proceso será simplemente:

cd /var/www/mi-aplicacion

git pull
npm ci
pm2 restart mi-app --update-env

Si nuestra aplicación necesita compilarse también tendremos que ejecutar:

npm run build

antes de reiniciar.

Por ejemplo:

git pull
npm ci
npm run build
pm2 restart mi-app --update-env

El procedimiento exacto dependerá de nuestro proyecto.


33. Ver el consumo de nuestra aplicación

PM2 incluye una interfaz de monitorización en terminal:

pm2 monit

También podemos consultar:

pm2 list

Ahí encontraremos información como:

  • Estado.
  • CPU.
  • Memoria.
  • Tiempo activo.
  • Reinicios.
  • PID.

También podemos utilizar herramientas del sistema:

top

o:

htop

si está instalado.


34. Ver logs de Nginx

Si nuestra aplicación funciona mediante:

127.0.0.1:3000

pero no mediante el dominio, deberíamos revisar Nginx.

Logs de acceso:

sudo tail -f /var/log/nginx/access.log

Logs de errores:

sudo tail -f /var/log/nginx/error.log

También podemos comprobar la configuración:

sudo nginx -t

35. Error 502 Bad Gateway

Uno de los problemas más habituales al desplegar Node.js detrás de Nginx es:

502 Bad Gateway

Normalmente significa que Nginx no puede conectarse con nuestra aplicación.

Comprobamos PM2:

pm2 list

Si la aplicación aparece detenida:

pm2 restart mi-app

Revisamos los logs:

pm2 logs mi-app

Después comprobamos directamente:

curl http://127.0.0.1:3000

Si esto falla, el problema probablemente está en Node.js y no en Nginx.


36. Comprobar que Node.js utiliza el puerto correcto

Ejecutamos:

sudo ss -tulpn | grep node

O:

sudo ss -tulpn | grep :3000

Si nuestra aplicación escucha en otro puerto debemos modificar:

proxy_pass http://127.0.0.1:3000;

para que coincida.

Por ejemplo, si Node.js utiliza:

4000

configuraremos:

proxy_pass http://127.0.0.1:4000;

Después:

sudo nginx -t
sudo systemctl reload nginx

37. Error EADDRINUSE

Podemos encontrarnos con un error parecido a:

EADDRINUSE: address already in use

Esto significa que otro proceso ya está utilizando el puerto.

Comprobamos:

sudo ss -tulpn | grep :3000

También podemos utilizar:

sudo lsof -i :3000

Si ya existe nuestra aplicación ejecutándose mediante PM2, no debemos iniciar otra copia manualmente con:

node app.js

38. La aplicación funciona por IP pero no por dominio

Primero comprobamos el DNS:

dig app.midominio.com

La IP debe coincidir con nuestra VPS.

Después comprobamos Nginx:

sudo nginx -t

Revisamos:

server_name app.midominio.com;

Y recargamos:

sudo systemctl reload nginx

También debemos recordar que los cambios DNS pueden necesitar tiempo para propagarse dependiendo del TTL y del proveedor utilizado.


39. La aplicación funciona pero PM2 no vuelve después de reiniciar

Comprobamos:

pm2 list

Después ejecutamos:

pm2 startup

Ejecutamos el comando que PM2 nos indique.

Finalmente:

pm2 save

Podemos comprobar el servicio generado mediante:

systemctl list-units | grep pm2

40. Reiniciar Nginx correctamente

Cuando cambiamos una configuración no conviene reiniciar Nginx inmediatamente sin comprobar primero la sintaxis.

Siempre deberíamos ejecutar:

sudo nginx -t

Si todo es correcto:

sudo systemctl reload nginx

reload permite cargar la nueva configuración sin realizar un reinicio completo innecesario del servicio.


41. Estructura final de nuestro servidor

Después de completar la guía podemos tener algo parecido a:

/var/www/
└── mi-aplicacion/
    ├── app.js
    ├── package.json
    ├── package-lock.json
    ├── node_modules/
    └── .env

Nuestra infraestructura:

Usuario
   │
   │ HTTPS
   ▼
Nginx
   │
   │ HTTP interno
   ▼
127.0.0.1:3000
   │
   ▼
Node.js
   │
   ▼
PM2

Y nuestros servicios principales:

Nginx       → puerto 80/443 público
Node.js     → puerto 3000 local
PM2         → mantiene Node.js funcionando
Certbot     → certificado SSL

42. Comandos útiles para administrar nuestra aplicación

Estado de PM2

pm2 list

Logs

pm2 logs mi-app

Reiniciar aplicación

pm2 restart mi-app

Detener aplicación

pm2 stop mi-app

Monitorizar recursos

pm2 monit

Guardar procesos

pm2 save

Comprobar Node.js

node -v

Comprobar Nginx

sudo nginx -t

Recargar Nginx

sudo systemctl reload nginx

Comprobar el backend

curl http://127.0.0.1:3000

Consultar puertos

sudo ss -tulpn

43. ¿Cuánta RAM necesita una aplicación Node.js?

No existe una cantidad universal.

Los recursos dependerán de:

  • Número de usuarios.
  • Tipo de aplicación.
  • Dependencias.
  • Uso de bases de datos.
  • Procesos en segundo plano.
  • Consumo de memoria.
  • Número de aplicaciones alojadas.
  • Compilaciones realizadas en el servidor.

Para aplicaciones pequeñas una VPS básica puede ser suficiente.

Aplicaciones con bases de datos, procesamiento intensivo, muchos usuarios o múltiples servicios necesitarán más CPU y RAM.

Podemos comprobar el consumo real mediante:

pm2 monit

y:

free -h

44. Desplegar varias aplicaciones Node.js en la misma VPS

Una ventaja de utilizar Nginx es que podemos alojar varias aplicaciones en un mismo servidor.

Por ejemplo:

api.midominio.com   → 127.0.0.1:3000
panel.midominio.com → 127.0.0.1:3001
chat.midominio.com  → 127.0.0.1:3002

Podemos crear diferentes bloques server de Nginx.

Por ejemplo:

server {
    listen 80;

    server_name api.midominio.com;

    location / {
        proxy_pass http://127.0.0.1:3000;

        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;
    }
}

Y otro:

server {
    listen 80;

    server_name panel.midominio.com;

    location / {
        proxy_pass http://127.0.0.1:3001;

        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;
    }
}

De esta forma una sola IP pública puede servir diferentes aplicaciones utilizando distintos dominios.


45. Node.js + Nginx frente a Docker

También podemos desplegar aplicaciones Node.js utilizando Docker.

Ambas opciones son válidas.

Una instalación tradicional:

Node.js
PM2
Nginx

es sencilla de entender y administrar para una única aplicación.

Con Docker podemos encapsular:

Aplicación
Node.js
Dependencias
Configuración

dentro de contenedores, algo especialmente útil cuando gestionamos diferentes proyectos o queremos despliegues reproducibles.

La elección dependerá de la arquitectura y necesidades del proyecto.


Desplegar Node.js en una VPS

Para publicar una aplicación Node.js necesitamos un servidor donde tengamos control sobre Node.js, Nginx y los procesos del sistema.

Una VPS nos permite disponer de:

  • Acceso root.
  • Dirección IP pública.
  • Node.js.
  • npm.
  • Nginx.
  • PM2.
  • Bases de datos.
  • Docker.
  • Certificados SSL.
  • Dominios y subdominios.

Si necesitas desplegar tu aplicación Node.js, puedes utilizar una VPS de ColdHosting con Ubuntu o Debian, conectarte mediante SSH y seguir esta guía desde el primer paso.


Conclusión

Desplegar una aplicación Node.js en una VPS requiere algo más que ejecutar:

node app.js

En un entorno de producción necesitamos asegurarnos de que nuestra aplicación permanezca funcionando, sea accesible mediante un dominio y utilice HTTPS.

En esta guía hemos configurado:

Node.js
   +
PM2
   +
Nginx
   +
Let's Encrypt
   =
Aplicación lista para producción

PM2 mantiene nuestra aplicación activa y permite recuperarla después de reiniciar el servidor.

Nginx funciona como reverse proxy y recibe las conexiones públicas mediante los puertos 80 y 443.

Nuestra aplicación Node.js permanece escuchando únicamente en:

127.0.0.1:3000

evitando exponer directamente el puerto de la aplicación a Internet.

Finalmente, mediante Certbot y Let's Encrypt podemos servir nuestra aplicación utilizando:

https://app.midominio.com

Antes de aplicar cualquier modificación en Nginx recuerda ejecutar siempre:

sudo nginx -t

Y si realizas cambios en tu aplicación:

pm2 restart mi-app

Con esta arquitectura ya tenemos una base sólida para desplegar aplicaciones Node.js en una VPS Ubuntu o Debian.