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:
- Creación del VPS desde el panel de Hetzner Cloud, selección de imagen Ubuntu LTS y región europea (Helsinki / Falkenstein / Nuremberg).
- Acceso SSH inicial con la clave pública que se cargó al crear el VPS.
- Endurecimiento básico: deshabilitar login root por contraseña, configurar usuario
deploycon permisos sudo, habilitarufwcon la lista de puertos definida en 5.3 (22 SSH, 80 HTTP, 443 HTTPS, 50000-50100/udp para WebRTC). - Instalación de Docker y Docker Compose.
- Clonación del repositorio en
/opt/quetzy/erp. - Provisión de
.env.productioncon los secretos del sistema (variables documentadas en 6.2 y 7.4), accesibles vía.env_filedesde Docker Compose. - Despliegue de Insforge BaaS fuera del Compose principal del ERP, alcanzable vía
host.docker.internal:7130. - Configuración del DNS en el registrador: registros A para
erp,internal,livekitapuntando a la IP del VPS. - Primer despliegue manual ejecutando
docker compose up -den/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:
lint: instala dependencias y ejecuta ESLint.test: ejecuta la suite Vitest completa (827 tests) sobre Node.js 20.build: ejecutanext buildy verifica que el bundle se compila correctamente. Depende delintytest.deploy: víaappleboy/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 main → docker compose build erp → docker compose up -d erp → health 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
| Recurso | Mínimo | Recomendado | Configuración real (CX33) |
|---|---|---|---|
| vCPUs | 2 | 4 | 4 (AMD EPYC-Rome) |
| RAM | 4 GiB | 8 GiB | 7,6 GiB |
| Almacenamiento | 40 GB SSD | 80 GB SSD | 75 GB SSD |
| Sistema operativo | Linux con kernel 5.x+ | Ubuntu LTS | Ubuntu Linux (kernel 6.8.0) |
| Conectividad | IPv4 pública | IPv4 + IPv6 | IPv4 + IPv6 (Hetzner) |
| Puertos abiertos | 22, 80, 443 | + 50000-50100/udp para WebRTC | Configuració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.
ufwo 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=mockpara 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/healthSi 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 conx-cron-secret(ver 7.2) y disparado por un cron externo (deuda futura: integrarlo dentro del propio sistema). En v1 se ejecuta manualmente o desdecronclá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-neten 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 dequetzy.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 logspara inspección puntual de cualquier servicio./api/healthcomo 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.