Después de años haciendo deploys manuales por SSH o con scripts básicos de GitHub Actions, decidí construir algo que me diera control total: Deployer, una plataforma para gestionar servidores y desplegar aplicaciones directamente desde una interfaz web.

El problema que quería resolver

Las herramientas existentes son o demasiado simples (solo hacen git pull + restart) o demasiado complejas y caras (Kubernetes, plataformas PaaS). Yo quería algo intermedio:

  • Ver el estado de mis aplicaciones en producción

  • Lanzar un deploy desde el navegador y ver los logs en tiempo real

  • Gestionar Nginx y PM2 de forma remota

  • Conectar con mis repos de GitHub sin copiar tokens manualmente

La arquitectura

El stack elegido: NestJS para el backend, Next.js para el frontend, PostgreSQL para persistencia, Socket.io para tiempo real.

Conexión SSH desde el servidor

El módulo más crítico. Usé la librería ssh2 en Node.js para establecer conexiones SSH a servidores remotos. El flujo:

  1. El usuario añade un servidor con su IP, puerto y credenciales

  2. Las credenciales se cifran con AES-256 antes de guardarse en base de datos

  3. Cuando se lanza un deploy, se establece una conexión SSH, se ejecutan los comandos y el output se emite por WebSocket

// Simplificado - ejecutar comando remoto y streamear output
async executeCommand(serverId: string, command: string, socketId: string) {
  const server = await this.getDecryptedServer(serverId);
  const conn = new Client();

  conn.on('ready', () => {
    conn.exec(command, (err, stream) => {
      stream.on('data', (data) => {
        this.gateway.emit(socketId, 'log', data.toString());
      });
      stream.on('close', () => conn.end());
    });
  }).connect(server.sshConfig);
}

Logs en tiempo real con WebSockets

Cada deploy abre un canal de Socket.io. El frontend se suscribe al room del deploy y recibe cada línea de log según llega. Sin polling, sin recargas.

El resultado: ves el output del deploy exactamente como si estuvieras en la terminal, pero desde el navegador.

Integración con GitHub

Conectar con la API de GitHub para:

  • Listar repositorios del usuario autenticado

  • Obtener branches y commits recientes

  • Disparar o consultar GitHub Actions workflows

Toda la autenticación via OAuth App para no pedir Personal Access Tokens.

Gestión de Nginx y PM2

Comandos remotos para las operaciones más comunes:

  • Reload/restart de Nginx tras un deploy

  • Restart de procesos PM2 por nombre

  • Ver logs de PM2 de una aplicación concreta

Lo más difícil

Seguridad de credenciales

Guardar contraseñas y claves privadas SSH en base de datos es delicado. Implementé cifrado simétrico AES-256-GCM con una clave maestra en variables de entorno. No es perfecto, pero es un equilibrio razonable para un uso personal/small team.

Manejo de streams SSH

Los streams de SSH no se comportan exactamente como streams de Node.js. Hay edge cases con buffers, con comandos que no terminan limpiamente, con sesiones que se quedan colgadas. Requirió mucho testing manual en servidores reales.

Qué aprendí

Construir tus propias herramientas te enseña cosas que usar las de otros no te enseña. Entiendes por qué Heroku cobra lo que cobra. Entiendes por qué Railway existe. Y entiendes en qué casos tiene sentido pagar por esas soluciones y en cuáles merece la pena tener el control total.

Deployer no va a reemplazar a Coolify o Dokku. Pero es mío, lo entiendo completamente, y hace exactamente lo que necesito.