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:
El usuario añade un servidor con su IP, puerto y credenciales
Las credenciales se cifran con AES-256 antes de guardarse en base de datos
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.