Logo QuetzyQuetzy TFG

9. Despliegue

STATUS: borrador (v1) Estimación: 4-5 páginas

9.1 Pasos para instalar o desplegar

El despliegue de Quetzy ERP en producción es completamente automatizado tras el primer aprovisionamiento del VPS. Esta sección documenta tanto el flujo automatizado como los pasos de instalación inicial necesarios para reproducir el entorno desde cero.

Aprovisionamiento inicial del VPS (one-time)

El VPS Hetzner CX33 se prepara una sola vez con los siguientes pasos manuales:

  1. Creación del VPS desde el panel de Hetzner Cloud, selección de imagen Ubuntu LTS y región europea (Helsinki / Falkenstein / Nuremberg).
  2. Acceso SSH inicial con la clave pública que se cargó al crear el VPS.
  3. Endurecimiento básico: deshabilitar login root por contraseña, configurar usuario deploy con permisos sudo, habilitar ufw con la lista de puertos definida en 5.3 (22 SSH, 80 HTTP, 443 HTTPS, 50000-50100/udp para WebRTC).
  4. Instalación de Docker y Docker Compose.
  5. Clonación del repositorio en /opt/quetzy/erp.
  6. Provisión de .env.production con los secretos del sistema (variables documentadas en 6.2 y 7.4), accesibles vía .env_file desde Docker Compose.
  7. Despliegue de Insforge BaaS fuera del Compose principal del ERP, alcanzable vía host.docker.internal:7130.
  8. Configuración del DNS en el registrador: registros A para erp, internal, livekit apuntando a la IP del VPS.
  9. Primer despliegue manual ejecutando docker compose up -d en /opt/quetzy/erp. Caddy provisiona automáticamente los certificados Let’s Encrypt en el primer arranque.

A partir de este momento, el VPS queda listo para recibir despliegues automáticos.

Despliegue continuo (automático)

El flujo de despliegue continuo se dispara con cada merge a main mediante GitHub Actions. El workflow .github/workflows/deploy.yml ejecuta cuatro jobs secuenciales:

  1. lint: instala dependencias y ejecuta ESLint.
  2. test: ejecuta la suite Vitest completa (827 tests) sobre Node.js 20.
  3. build: ejecuta next build y verifica que el bundle se compila correctamente. Depende de lint y test.
  4. deploy: vía appleboy/ssh-action@v1, conecta por SSH al VPS con clave gestionada como GitHub Secret y ejecuta el script /opt/quetzy/erp/scripts/deploy.sh.

El script deploy.sh realiza, en este orden: git pull origin maindocker compose build erpdocker compose up -d erphealth check contra /api/health con espera y reintentos. Si el health check falla, registra el error pero NO ejecuta rollback automático: el operador humano dispara el rollback manualmente revirtiendo el commit y volviendo a desplegar.

El descubrimiento de que este flujo estaba ya activo y en pleno funcionamiento se produjo durante el desarrollo: durante varias semanas el desarrollador hizo despliegues manuales redundantes vía SSH sin saber que GitHub Actions ya estaba aplicando los cambios al hacer merge. El hallazgo está documentado como nota de aprendizaje en la deuda técnica del proyecto y motivó una mejora de la documentación interna del repositorio.

9.2 Requisitos del entorno

Requisitos del servidor

RecursoMínimoRecomendadoConfiguración real (CX33)
vCPUs244 (AMD EPYC-Rome)
RAM4 GiB8 GiB7,6 GiB
Almacenamiento40 GB SSD80 GB SSD75 GB SSD
Sistema operativoLinux con kernel 5.x+Ubuntu LTSUbuntu Linux (kernel 6.8.0)
ConectividadIPv4 públicaIPv4 + IPv6IPv4 + IPv6 (Hetzner)
Puertos abiertos22, 80, 443+ 50000-50100/udp para WebRTCConfiguración completa según 5.3

Requisitos de software en el servidor

  • Docker Engine 24.x o superior.
  • Docker Compose v2.x.
  • Git 2.x.
  • ufw o equivalente (firewall a nivel sistema operativo).
  • Acceso SSH con autenticación por clave pública (no por contraseña).

Requisitos del cliente

  • Navegador moderno con soporte para Web Audio API y getUserMedia (Chromium >= 100, Firefox >= 100, Safari >= 16). Las llamadas WebRTC y la grabación de audio dependen de estas APIs.
  • Conexión que permita UDP saliente al rango 50000-50100 del VPS, o TCP saliente al puerto 7881 de fallback. En redes corporativas con NAT estricto puede requerirse un servidor TURN intermedio (deuda explícita, ver 3.6).

Requisitos para entornos de desarrollo local

  • Node.js 20 LTS.
  • npm 10.x o superior.
  • Docker Desktop (opcional, solo si se desea levantar Insforge y LiveKit en local).
  • Variable de entorno ERP_DATA_SOURCE=mock para arrancar sin BD real (ver 6.1).

9.3 Scripts o automatizaciones

El sistema utiliza tres niveles de automatización:

Pipeline CI/CD en GitHub Actions

Definido en .github/workflows/deploy.yml. Se ejecuta en cada push a main y en cada pull_request (los jobs lint, test y build; el job deploy solo se dispara tras merge a main).

Pre-push hook local

El proyecto usa un hook personalizado denominado ECC pre-push que ejecuta npm run lint y npm test antes de cada git push. Este hook reduce la fricción de descubrir fallos solo en CI, especialmente útil cuando un agente IA acaba de generar cambios. La salida de un push exitoso muestra:

[ECC pre-push] Node project detected (package manager: npm) [ECC pre-push] Running: lint [ECC pre-push] Running: test [ECC pre-push] Running: build [ECC pre-push] Verification checks passed.

Si cualquiera de los tres pasos falla, el push se aborta antes de subir el código al remoto.

Script de despliegue en VPS

/opt/quetzy/erp/scripts/deploy.sh es el script ejecutado por GitHub Actions tras autenticarse vía SSH. Pseudo-código simplificado:

#!/usr/bin/env bash set -e cd /opt/quetzy/erp git pull origin main docker compose build erp docker compose up -d erp sleep 5 curl --fail --retry 5 --retry-delay 3 https://erp.quetzy.eu/api/health

Si el curl final falla, el script termina con código de error y GitHub Actions marca el job deploy como fallido. La intervención humana decide si se hace rollback (revertir el commit) o si se investiga in situ en el VPS.

Otras automatizaciones

  • Cron de notificaciones (/api/cron/notifications): autenticado con x-cron-secret (ver 7.2) y disparado por un cron externo (deuda futura: integrarlo dentro del propio sistema). En v1 se ejecuta manualmente o desde cron clásico del VPS.
  • Renovación de certificados TLS: gestionada automáticamente por Caddy mediante ACME, sin intervención humana.

9.4 Hosting o arquitectura final

La arquitectura de despliegue final se compone de los siguientes elementos, ya documentados en detalle en 5.2 (arquitectura del sistema) y 5.3 (red e infraestructura). Esta sección los recapitula desde la perspectiva operativa de despliegue.

Capa de infraestructura

  • Proveedor: Hetzner Cloud (Alemania, RGPD-compliant).
  • Instancia: CX33 (4 vCPU, 7,6 GiB RAM, 75 GB SSD).
  • Sistema operativo: Ubuntu Linux con kernel 6.8.0.
  • Coste mensual: fijo, sin sorpresas por tráfico ni por número de usuarios. Esta predictibilidad fue uno de los criterios clave para descartar PaaS y serverless (ver RNF-09 y discusión en 4.3).

Capa de orquestación

  • Docker Compose gestionando tres servicios principales: erp (build local), livekit (livekit/livekit-server:v1.11.0), caddy (caddy:2-alpine).
  • Insforge BaaS corre fuera del Compose principal y expone su interfaz HTTP en el puerto 7130 del host, alcanzable desde los contenedores vía host.docker.internal.
  • Red Docker quetzy-net en modo bridge. Sin subnets customizadas.

Capa de routing y TLS

  • Caddy como punto único de entrada para los tres dominios (erp.quetzy.eu, internal.quetzy.eu, livekit.quetzy.eu) más el redirect de quetzy.eu.
  • TLS automático con Let’s Encrypt vía protocolo ACME. HTTP/3 habilitado.
  • No hay CDN intermedio en v1. El tráfico va directo cliente → Caddy → backend correspondiente. Para una eventual fase de tráfico público alto se evaluaría Cloudflare en modo proxy.

Capa de aplicación

  • Next.js 16.2.4 en modo standalone (output optimizado de Next que reduce el tamaño de imagen Docker).
  • PostgreSQL 15.15 embebido en Insforge BaaS, no expuesto al exterior (puertos 5432 y 5430 bloqueados por UFW tras el incidente del 6 de mayo de 2026, ver 7.6).

Capa de monitorización (mínima)

En v1 no hay infraestructura de monitorización formal. La observabilidad se basa en:

  • docker logs para inspección puntual de cualquier servicio.
  • /api/health como health endpoint de la aplicación.
  • Logs de LiveKit accesibles vía docker logs livekit (críticos durante el diagnóstico del bug de audio asimétrico, ver 7.6).
  • Notificaciones manuales vía email del proveedor (Hetzner) y de auditorías externas (BSI alemán).

Como deuda explícita queda la integración de un stack de observabilidad (Prometheus + Grafana, o equivalente self-hosted como Uptime Kuma) post-TFG.

Diagrama de despliegue final

El diagrama de la Figura 5.3 (Cap 5 §5.2) representa con precisión la arquitectura de despliegue final del sistema. No se incluye aquí para evitar duplicación.