This website uses cookies

Our website, platform and/or any sub domains use cookies to understand how you use our services, and to improve both your experience and our marketing relevance.

La Agencia de Nueva Generación ya está aquí. Únete a más de 3.000 profesionales de agencias en Agency Advantage 2026. Regístrate gratis→

Cómo implementar una aplicación WebSocket de Node.js con PM2 y Fastify

Actualizado el Septiembre 22, 2026

13 min de lectura

Puntos clave

  • La baja sobrecarga de Fastify lo hace ideal para mantener un gran número de conexiones WebSocket abiertas sin tener problemas de memoria.
  • PM2 mantiene activo un proceso de Node.js en producción con recuperación instantánea tras fallos, reinicios sin tiempo de inactividad y una gestión limpia de los registros.
  • Un pequeño panel de telemetría creado con Fastify y una ruta WebSocket muestra cómo se transmiten datos en tiempo real a un navegador sin necesidad de sondeos.
  • Cloudways Velocity se encarga automáticamente de la daemonización de PM2 y del enrutamiento mediante proxy inverso, por lo que una aplicación WebSocket de Node.js puede pasar de GitHub a producción sin necesidad de configurar manualmente el servidor.

Crear una aplicación de Node.js en tiempo real en tu ordenador local es bastante sencillo. Solo tienes que instalar un paquete, abrir un puerto y empezar a enviar datos a un cliente. Sin embargo, cuando se trata de implementar esa misma aplicación en producción, ahí es donde las cosas se complican.

A diferencia de las peticiones HTTP normales, que se completan en milisegundos, los WebSockets mantienen las conexiones abiertas mientras el cliente y el servidor necesiten comunicarse. Si tu aplicación se cuelga o se detiene el proceso de Node.js, todos los clientes conectados pierden la conexión al instante.

Para que una aplicación de Fastify funcione de forma fiable en producción, no basta con un marco de trabajo ligero. También necesitas un gestor de procesos que mantenga tu aplicación en marcha en segundo plano y la reinicie automáticamente si surge algún problema.

En esta guía, te explicaré cómo gestiona Fastify los WebSockets, por qué PM2 para Node.js es una parte importante a la hora de ejecutar aplicaciones en producción, crearé un proyecto sencillo de WebSockets con Node.js y, después, implementaré toda la pila en Cloudways Velocity, donde PM2 ya viene integrado.

Cómo afectan los WebSockets a tu infraestructura de servidores

Las arquitecturas HTTP REST estándar funcionan siguiendo un ciclo de vida estricto de solicitud-respuesta. Un cliente inicia un protocolo de enlace TCP, envía los encabezados y los datos del cuerpo, espera la respuesta del servidor y cierra la conexión.

Si un cliente necesita datos actualizados cada pocos segundos, tiene que repetir todo ese proceso de establecimiento de conexión una y otra vez.

Los WebSockets dan un giro radical al HTTP tradicional. En lugar de abrir y cerrar una nueva solicitud cada vez que necesitas datos, empiezan con un rápido establecimiento de conexión HTTP, actualizan el protocolo y dejan ese canal totalmente abierto.

Conexión HTTP Conexión WebSocket
Cliente → Solicitud → Servidor Cliente → Solicitud de actualización → Servidor
Cliente ← Respuesta ← Servidor Cliente ← 101 Protocolos de conmutación ← Servidor
Se cierra la conexión La conexión se mantiene abierta
Se crea una nueva conexión para cada solicitud Se reutiliza la misma conexión
La comunicación es solo de tipo «solicitud → respuesta» El cliente y el servidor pueden intercambiar datos en ambas direcciones en cualquier momento

Bueno, mantener una conexión abierta para siempre suena genial hasta que te fijas en lo que realmente le hace a tu servidor entre bastidores:

La memoria se va agotando rápidamente. Cada socket que mantengas abierto ocupa RAM. Si tu framework está sobrecargado, tu servidor se quedará sin margen de maniobra mucho antes de que alcances una escala importante.

Un solo fallo te deja a todos fuera de combate. Con las API REST estándar, si se produce un error no gestionado en una solicitud, un usuario ve un error 500. Pero, ¿y si se cuelga un servidor WebSocket sin supervisar? ¡Pum! Todo el proceso se va al traste y miles de usuarios activos se quedan sin conexión exactamente en el mismo milisegundo.

A los proxies les encanta cortar las conexiones inactivas. Los equilibradores de carga, los cortafuegos y los proxies en la nube cortan silenciosamente las conexiones TCP si no hay tráfico durante un par de minutos. Tienes que enviar señales periódicas de «ping/pong» solo para mantener el canal activo.

Ahí es precisamente donde Fastify destaca. Como su árbol de enrutamiento central y el análisis de JSON prácticamente no tienen sobrecarga, usa mucha menos memoria por conexión que los frameworks más antiguos, lo que lo hace ideal para mantener abiertas cantidades enormes de sockets.

Por qué PM2 es imprescindible para Node.js en producción

Como Node se ejecuta en un único hilo de bucle de eventos, un rechazo sin gestionar hará que se cuelgue toda tu aplicación. Y punto.

Y, obviamente, no puedes simplemente conectarte por SSH a un servidor en producción, ejecutar «node server.js» en una ventana de terminal y cerrar el portátil. En cuanto se cierre la sesión de la terminal, tu app se caería con ella.

Ahí es donde entra en juego PM2. Imagínatelo como un supervisor que está justo encima de tu aplicación Node y se encarga de que siga funcionando pase lo que pase:

Ejecución en segundo plano: convierte tu proceso en un servicio para que se ejecute de forma silenciosa en segundo plano las 24 horas del día, los 7 días de la semana.

Recuperación inmediata tras un fallo: si se cuela un error inesperado y bloquea el hilo, PM2 reactiva inmediatamente la instancia en milisegundos.

Actualizaciones sin interrupciones: puedes implementar código nuevo y reiniciar las instancias una tras otra, para que ningún usuario pierda la conexión en pleno funcionamiento.

Registro limpio: divide tus registros estándar y flujos de error en archivos limpios y persistentes sin que tengas que configurar flujos personalizados.

Normalmente, configurar todo esto en un servidor Linux sin nada instalado implica lidiar con los archivos de servicios de systemd, crear scripts personalizados de ecosystem.config.js, configurar la rotación de logs y configurar manualmente los proxies inversos de Nginx. Sin embargo, con Cloudways Velocity, no tienes que ocuparte de nada de eso: la demonización de PM2 y el enrutamiento del proxy se gestionan automáticamente en segundo plano cuando realizas el despliegue.

Olvídate del trabajo de DevOps y lanza tu aplicación directamente

Cloudways Velocity se encarga automáticamente de la gestión de procesos de PM2 y del proxy inverso, para que tu aplicación esté siempre en línea sin necesidad de configuraciones manuales.

Mini proyecto: aplicación WebSocket en tiempo real con Node.js y Fastify

Para mostrarte cómo funciona la implementación de WebSocket con Fastify, voy a crear un mini proyecto a modo de ejemplo. Lo haré de forma sencilla: crearé un panel de telemetría en tiempo real que recoja el uso de memoria y el tiempo de actividad del servidor y lo muestre en una página web.

Para esta configuración, primero voy a compilarlo y probarlo todo localmente con VS Code y mi entorno local de Node. Después lo subiré a GitHub y lo pondré en producción en Cloudways Velocity.

Paso 1: Configurar el entorno de Fastify

Es hora de preparar la carpeta. Como tengo restricciones informáticas en mi portátil del trabajo, estoy usando un binario independiente de Node.js. Esto significa que, por defecto, mi Símbolo del sistema no sabe dónde están mis herramientas de Node.

Para solucionarlo, voy a abrir el CMD y le diré exactamente dónde he descomprimido mi carpeta de Node. En mi caso, está aquí:

set PATH=%PATH%;C:\Users\abdulrehman\Downloads\node-v24.18.0-win-x64\node-v24.18.0-win-x64

Símbolo del sistema mostrando el comando PATH que apunta a la carpeta de Node.js

Una vez que haya seleccionado la carpeta de Node, crearé la carpeta de mi proyecto e inicializaré la aplicación de Node.

mkdir fastify-pm2-demo
cd fastify-pm2-demo
npm init -y

Salida de la terminal tras ejecutar «npm init» para crear el archivo package.json del proyecto

Ahora voy a instalar el framework Fastify y el plugin de WebSocket con este comando:

npm install fastify @fastify/websocket

Salida de la terminal tras instalar Fastify y el complemento WebSocket

Una vez hecho esto, voy a añadir unos scripts al archivo package.json para poder iniciar la aplicación con «npm run dev» durante el desarrollo y con «npm start» en producción.

{
"name": "fastify-pm2-demo",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"start": "node server.js",
"dev": "node --watch server.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"type": "commonjs",
"dependencies": {
"@fastify/websocket": "^11.3.0",
"fastify": "^5.11.0"
}
}

Y con esto, ya tengo listo el entorno básico para mi app.

Paso 2: Crear el servidor WebSocket

Ahora voy a abrir la carpeta del proyecto que acabo de crear en VS Code.

VS Code mostrando la carpeta del proyecto «fastify-pm2-demo» que acabas de crear

Dentro de la carpeta, voy a crear un archivo llamado server.js. Este será el archivo que contendrá toda la lógica del backend.

Este es el código que voy a añadir a mi archivo server.js:

const Fastify = require('fastify');
const fastifyWebsocket = require('@fastify/websocket');
const fs = require('fs');
const path = require('path');
const fastify = Fastify({ logger: true });
fastify.register(fastifyWebsocket, {
options: { maxPayload: 1048576 }
});
// Serve the HTML dashboard on the root URL
fastify.get('/', (req, reply) => {
reply.type('text/html').send(fs.createReadStream(path.join(__dirname, 'index.html')));
});
fastify.register(async function (app) {
app.get('/ws', { websocket: true }, (socket, req) => {
app.log.info('Client connected to WebSocket stream');
socket.isAlive = true;
socket.on('pong', () => {
socket.isAlive = true;
});
const interval = setInterval(() => {
if (socket.readyState === 1) {
const memoryUsage = process.memoryUsage();
socket.send(JSON.stringify({
timestamp: new Date().toISOString(),
uptime: Math.floor(process.uptime()),
heapUsedMb: (memoryUsage.heapUsed / 1024 / 1024).toFixed(2)
}));
}
}, 1000);
socket.on('close', () => {
app.log.info('Client disconnected');
clearInterval(interval);
});
socket.on('error', (err) => {
app.log.error(`Socket error: ${err.message}`);
clearInterval(interval);
});
});
});
const pingInterval = setInterval(() => {
if (!fastify.websocketServer) return;
fastify.websocketServer.clients.forEach((socket) => {
if (socket.isAlive === false) {
return socket.terminate();
}
socket.isAlive = false;
socket.ping();
});
}, 30000);
fastify.addHook('onClose', (instance, done) => {
clearInterval(pingInterval);
done();
});
const start = async () => {
try {
const port = process.env.PORT || 3000;
const host = process.env.HOST || '0.0.0.0';
await fastify.listen({ port: Number(port), host });
console.log(\`Server running on http://host:{host}: host:{port}\`);
} catch (err) {
fastify.log.error(err);
process.exit(1);
}
};
start();

El editor VS Code muestra el código WebSocket completo de server.js

Lo que va a hacer este código es abrir una ruta en /ws. Y cada segundo, comprobará la memoria del servidor y difundirá esos datos a todos los que estén conectados.

Ah, y también he añadido un mecanismo de ping. Con esto me refiero a que comprueba cada 30 segundos si hay conexiones inactivas para cortarlas, así la memoria del servidor no se va agotando poco a poco hasta que se cuelga.

Paso 3: Crear el panel de control HTML

Ahora voy a crear la lógica para mostrar los datos que se van a recuperar del servidor.

Para ello, voy a crear un archivo index.html justo al lado de mi archivo del servidor. Voy a escribir un poco de JavaScript básico que se conecte con el backend. Básicamente, cuando reciba un mensaje, colocará los valores en unos elementos HTML con estilo definido mediante CSS.

Así que… en lugar de dejar que se cuelgue si el servidor se reinicia, he añadido una lógica de reconexión. Si se corta la conexión, intentará volver a conectarse cada tres segundos.

Este es el código que voy a añadir a mi archivo index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Fastify WebSocket Dashboard</title>
  <style>
    body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; background: #f4f4f5; padding: 2rem; color: #333; }
    .card { background: white; padding: 1.5rem; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); max-width: 400px; margin: 0 auto; }
    h1 { font-size: 1.25rem; margin-top: 0; margin-bottom: 1.5rem; text-align: center; }
    .status-container { text-align: center; margin-bottom: 1.5rem; }
    .status { font-weight: bold; padding: 6px 12px; border-radius: 999px; display: inline-block; font-size: 0.875rem; }
    .status.connected { background: #dcfce7; color: #166534; }
    .status.disconnected { background: #fee2e2; color: #991b1b; }
    .metric { display: flex; justify-content: space-between; margin-bottom: 0.75rem; border-bottom: 1px solid #e4e4e7; padding-bottom: 0.75rem; }
    .metric:last-child { border-bottom: none; margin-bottom: 0; padding-bottom: 0; }
  </style>
</head>
<body>
  <div class="card">
    <h1>Server Dashboard</h1>

    <div class="status-container">
      <div id="status" class="status disconnected">Disconnected</div>
    </div>

    <div class="metric">
      <span>Server Uptime</span>
      <strong id="uptime">0s</strong>
    </div>

    <div class="metric">
      <span>Heap Memory Usage</span>
      <strong id="heap">0 MB</strong>
    </div>

    <div class="metric">
      <span>Last Ping</span>
      <strong id="ping">--:--:--</strong>
    </div>
  </div>

  <script>
    // Dynamically grab the current host/port so it works on localhost, 127.0.0.1, or a live server
    const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
    const wsUrl = `${wsProtocol}//${window.location.host}/ws`;
    let ws;

    function connect() {
      console.log(`Attempting to connect to ${wsUrl}...`);
      ws = new WebSocket(wsUrl);

      ws.onopen = () => {
        console.log('WebSocket connected!');
        const statusEl = document.getElementById('status');
        statusEl.textContent = 'Connected';
        statusEl.className = 'status connected';
      };

      ws.onmessage = (event) => {
        try {
          const data = JSON.parse(event.data);

          // Update the DOM with the live data
          document.getElementById('uptime').textContent = data.uptime + 's';
          document.getElementById('heap').textContent = data.heapUsedMb + ' MB';

          const date = new Date(data.timestamp);
          document.getElementById('ping').textContent = date.toLocaleTimeString();
        } catch (err) {
          console.error("Error parsing WebSocket message:", err);
        }
      };

      ws.onclose = () => {
        console.log('WebSocket disconnected. Retrying in 3 seconds...');
        const statusEl = document.getElementById('status');
        statusEl.textContent = 'Disconnected';
        statusEl.className = 'status disconnected';

        // Auto-reconnect if the connection drops
        setTimeout(connect, 3000);
      };

      ws.onerror = (error) => {
        console.error('WebSocket Error:', error);
      };
    }

    // Initialize connection
    connect();
  </script>
</body>
</html>

El editor VS Code muestra el código del panel de control de index.html

Paso 4: Ejecutar y probar localmente

Es hora de probarlo. Vuelvo al CMD y voy a iniciar el servidor de desarrollo:

npm run dev

Después abriré http://localhost:3000 en mi navegador.

Si todo ha funcionado, el panel de control se vuelve verde en cuanto se carga la página y los números empiezan a subir cada segundo.

Panel de control en tiempo real que muestra el estado de conexión, con datos actualizados sobre el tiempo de actividad y la memoria

Lo que pasa es que el JavaScript ha abierto una conexión persistente con el servidor Fastify, y ahora el servidor simplemente envía datos actualizados por esa conexión cada segundo. El navegador no está pidiendo actualizaciones, sino que se le envían directamente.

¿Quieres ver cómo se activa la lógica de desconexión? Voy a parar el servidor en la terminal. El panel de control se pone rojo por sí solo, sin necesidad de actualizar la página.

El indicador de estado del panel de control se pone rojo cuando el servidor WebSocket deja de funcionar

Vuelve a ponerlo en marcha y se vuelve a conectar solo. Sigue sin hacer falta actualizar.

El panel de control se vuelve a conectar automáticamente después de reiniciar el servidor

Paso 5: Subir el código a GitHub

El código funciona localmente, así que ahora lo voy a subir a GitHub.

Pero antes de nada, voy a añadir un archivo .gitignore con los módulos de Node. Si no, acabaría metiendo miles de archivos de dependencias en el repositorio, y eso no me interesa.

node_modules/

Editor de texto que muestra el archivo .gitignore con «node_modules» añadido

Después, solo tienes que usar los comandos habituales de Git para subir todo:

git init
git add .
git commit -m "Initial commit for Fastify WebSocket application"
git branch -M main
git remote add origin https://github.com/your-username/fastify-pm2-demo
git push -u origin main

Actualizo la página del repositorio y ahí están todos mis archivos.

Página del repositorio de GitHub donde se ven los archivos del proyecto Fastify que se han subido

Paso 6: Implementación en Cloudways Velocity

Ahora que el proyecto está en GitHub, puedo implementarlo en el alojamiento de Node.js de Cloudways (Velocity).

Voy a iniciar sesión en Cloudways, buscaré «Velocity» en el menú de la izquierda y haré clic en «Empezar».

Panel de control de Cloudways en el que se ve el menú «Velocity» y el botón «Empezar»

El plan «Starter» es más que suficiente para esto, así que lo elegiré y haré clic en «Continuar».

Pantalla de selección del plan Velocity de Cloudways con el plan Starter seleccionado

Ahora quiere saber dónde está mi código. Cloudways te ofrece GitHub, GitLab o Bitbucket, y el mío está en GitHub, así que ese es el que voy a conectar.

Pantalla de Cloudways para conectar un repositorio de GitHub, GitLab o Bitbucket

En el menú desplegable, voy a seleccionar mi repositorio «fastify-pm2-demo» y haré clic en «Continuar».

Menú desplegable del repositorio de Cloudways en el que aparece seleccionado el proyecto «fastify-pm2-demo»

Cloudways detecta el proyecto y rellena los ajustes automáticamente. Les echaré un vistazo rápido y haré clic en «Deploy Now».

Pantalla de configuración de implementación de Cloudways con el botón «Implementar ahora»

A partir de ahí, descarga el código, inicia la aplicación con PM2 y configura el proxy inverso para reenviar el tráfico de WebSocket a la aplicación Node. Sin configuración manual de PM2, sin tocar Nginx, que es precisamente la ventaja de usar Velocity para esto.

Pantalla de progreso de la implementación de Cloudways mientras la aplicación se implementa con PM2

Paso 7: Prueba final en producción

Una vez finalizada la implementación, Cloudways te proporciona una URL temporal para la aplicación.

Pantalla de Cloudways en la que se muestra la URL temporal de la aplicación desplegada

Voy a copiar eso y lo abriré en mi navegador. Como el JavaScript del frontend comprueba el protocolo por sí solo, el cambio a una conexión segura wss:// se hace automáticamente, sin que yo tenga que configurar nada.

Se ha cargado en el navegador el panel de control de Fastify WebSocket, que se ha implementado a través de WSS

La página se carga, el icono se pone verde y los datos de telemetría empiezan a transmitirse, exactamente igual que lo hacían en mi ordenador.

Panel de telemetría en directo que muestra en tiempo real el tiempo de actividad y los datos de memoria tras la implementación

Y ahí tienes una aplicación Fastify WebSocket lista para producción, que se ejecuta con PM2 para garantizar su estabilidad, en directo en el alojamiento Node.js de Cloudways (Velocity).

Pon tu aplicación Node.js en producción hoy mismo

Disfruta de la recuperación tras fallos de PM2 y del proxy automático para WebSocket desde el primer momento con Cloudways Velocity.

Conclusión

Así es como se lleva una aplicación en tiempo real desde tu portátil a un servidor de producción. Expliqué cómo Fastify te proporciona la velocidad necesaria para mantener muchas conexiones WebSocket abiertas sin apenas sobrecarga, y cómo PM2 es el componente que evita que esas conexiones se interrumpan cada vez que la aplicación encuentra un error o el proceso falla.

El miniproyecto lo integró todo: primero se ejecutó una transmisión de telemetría en vivo de forma local y luego se implementó en Cloudways Velocity, donde la parte de PM2 ya está gestionada, por lo que no hay que preocuparse por la configuración manual.

El código fuente completo está en mi GitHub por si quieres clonarlo. Y si te atascas en algún momento, ya sea con la configuración de Fastify, las partes relacionadas con WebSocket o la propia implementación, deja un comentario aquí abajo y te echaré una mano.

P. ¿Para qué sirve Fastify?

Es un framework ligero de Node.js para crear APIs y páginas web dinámicas. Es rápido, tiene un consumo de recursos reducido y está diseñado en torno a plugins, así que solo incorporas los componentes que tu aplicación realmente necesita.

P. ¿Para qué sirve PM2?

Mantiene las aplicaciones de Node.js en ejecución en segundo plano y las reinicia si se cuelgan. Además, te ofrece un equilibrador de carga integrado, recargas sin tiempo de inactividad y gestión automática de registros; básicamente, todo aquello que prefieres no tener que gestionar a mano en un servidor en producción.

P. ¿De verdad es Fastify más rápido que Express?

En la mayoría de las pruebas de rendimiento, sí. Express suele rondar entre 10.000 y 20.000 solicitudes por segundo, mientras que Fastify se acerca más a las 45.000 o 50.000. Gran parte de esa diferencia se debe a su serialización JSON y al enrutamiento mediante árboles radix. Que lo notes o no depende de tu tráfico, pero la diferencia es real.

P. ¿Cómo se activa la supervisión con PM2?

Añade el parámetro –watch al iniciar la aplicación: pm2 start app.js –watch. O configúralo de forma permanente añadiendo watch: true en el archivo de configuración de tu ecosistema.

P. ¿Fastify es bueno para producción?

Sí que lo es. La validación de esquemas ayuda mucho, ya que descarta las peticiones mal formadas antes de que lleguen a tu lógica, así que ganas un poco en seguridad y velocidad sin ningún esfuerzo. Entre eso, los plugins y el HTTP/2 nativo, aguanta muy bien bajo carga real.

P. ¿Qué empresas usan Fastify?

Hay unas cuantas grandes, como Capital One, Walmart, American Express, Voodoo y Handsontable, entre otras.

Share your opinion in the comment section. COMMENT NOW

Share This Article

Start Growing with Cloudways Today.

Our Clients Love us because we never compromise on these

[email protected]

Abdul es un experto en tecnología, aficionado al café y al marketing creativo al que le encanta estar al día de las últimas actualizaciones de software y aparatos tecnológicos. También es un hábil escritor técnico capaz de explicar conceptos complejos de forma sencilla para un público amplio. Abdul disfruta compartiendo sus conocimientos sobre el sector de la Nube a través de manuales de usuario, documentación y entradas de blog.

×

Webinar: How to Get 100% Scores on Core Web Vitals

Join Joe Williams & Aleksandar Savkovic on 29th of March, 2021.

Do you like what you read?

Get the Latest Updates

Share Your Feedback

Please insert Content

Thank you for your feedback!

Do you like what you read?

Get the Latest Updates

Share Your Feedback

Please insert Content

Thank you for your feedback!

Want to Experience the Cloudways Platform in Its Full Glory?

Take a FREE guided tour of Cloudways and see for yourself how easily you can manage your server & apps on the leading cloud-hosting platform.

Start my tour