SignBoox SignBoox
Infraestructura · referencia interna
Referencia de arquitectura

Cómo está montada
la plataforma SignBoox

Tres proveedores, nueve piezas, y el camino que hace cada request — desde la pantalla colgada en un local hasta la base de datos.

409 pantallas 60 clientes 1,3 req/s 53 GiB de media 2 mercados · ES · AR Septiembre 2026

El mapa de conexiones

Quién consume Cloudflare · DNS · CDN Laravel Cloud · AWS Frankfurt (UE) DigitalOcean · FRA1 409 players Android App nativa en cada pantalla · caché local Panel CMS Navegador · React Donde el cliente carga el contenido Menu App Navegador · React Cartas digitales Pages app.signboox.com menu.signboox.com Deploy en cada push · gratis R2 + CDN staticv2.signboox.com 53 GiB de media 33.402 archivos Builds del player Assets de las 9 apps Egress $0 PoPs en Madrid y Buenos Aires: los dos mercados, servidos desde el borde. API v3 apiv3.signboox.com Laravel 12 · el cerebro Tenants, pantallas, contenidos, auth · 2 réplicas Static v2 staticv2.signboox.com Laravel 12 · ingesta de media y builds 2 réplicas Horizon ×2 Las colas de los dos backends, en procesos aparte del web MySQL 8.4 Gestionada · un cluster, dos schemas Backups automáticos Valkey ×2 Redis gestionado · caché, sesiones, colas Es PaaS: nadie parchea un sistema operativo. El peor incidente de la historia del sistema fue exactamente eso, en máquina propia. 2 réplicas por servicio → los deploys no cortan. Caja de Reverb ws.apiv3.signboox.com WebSockets en tiempo real · ~400 conexiones permanentes. El panel manda el cambio, la pantalla lo aplica. Caja de ffmpeg / x265 Transcodifica video fuera del plano de aplicación. Pollea trabajos: un pico de video no puede tirar la API. Sin credenciales de base de datos. Las dos cargas que el PaaS no puede correr — y no por precio: el WebSocket gestionado no acepta dominio propio, y ffmpeg necesita disco, que ahí no existe. 1 2 3 4 5 6 7 8 9 10 11 12 13
Conexión en producción hoy Pendiente del cutover Los números explican cada flecha ↓

Qué hace cada conexión

1

Pantallas → API v3. Un latido cada 300 s, la playlist que les toca y qué versión de app deberían tener. Son las 409 pantallas × 300 s = los 1,3 req/s de toda la plataforma.

2

Pantallas → R2 + CDN. Bajan el video y las imágenes del borde. Nunca descargan durante la reproducción: por eso la caída de agosto no apagó una sola pantalla.

3

Pantallas y paneles → Reverb. Un WebSocket abierto y permanente. Es lo que hace que un cambio en el panel se vea en la pantalla al instante, sin esperar el latido.

4

Navegadores → Pages. El HTML y el JS de los dos fronts, servidos del CDN. Antes esto se buildeaba dentro del servidor de producción.

5

Panel → API v3. Toda la operación del CMS. Es el único que siente la latencia; las pantallas no la notan.

6

Panel → Static v2. La subida de media: hoy un POST con el archivo entero.

7

Panel → R2, directo. Subida prefirmada, sin pasar por la app. Pendiente: es lo último que falta para que producción acepte media nueva.

8

Static → API v3. El static no tiene usuarios propios: valida cada token preguntándole a la API. Va cacheado, porque estaba en el camino de todo upload.

9

Los tres → MySQL y Valkey. Base y caché gestionados, en el mismo datacenter. La base son 3 GB, de los cuales 3 son logs que nadie consulta.

10

API v3 → Reverb. La API publica el evento y Reverb lo reparte al parque.

11

ffmpeg → Static v2. La caja pide trabajo, avisa cuando terminó y avisa si falló. Sólo tres rutas HTTP: no ve la base, no ve la red interna.

12

ffmpeg ↔ R2. Baja el original del bucket y sube las versiones convertidas. Los GB de video nunca pasan por la aplicación.

13

Static v2 ↔ R2. Escribe lo que sube el panel, y cuando alguien pide un archivo devuelve un redirect al CDN. El servidor no sirve bytes: por eso su CPU está en 0,15%.

Las tres cosas que este dibujo explica

Los bytes no pasan por la aplicación

Conexiones 2 · 12 · 13

  • Video, imágenes y builds van del bucket al borde, y del borde a la pantalla.
  • El servidor sólo dice dónde está el archivo.
  • Es lo que mantiene la factura plana y el CPU en cero.

Cada caja falla sola

Conexiones 11 · 3

  • Un pico de transcodificación no tiene forma de tirar la API: son máquinas distintas y hablan por tres rutas HTTP.
  • Si Reverb se cae, las pantallas siguen reproduciendo y vuelven a sincronizar por el latido.

El parque no depende de la nube

Diseño del player

  • Las pantallas reproducen de su caché local y, si el sync falla, mantienen la playlist anterior.
  • La caída de dos días de agosto no interrumpió una sola pantalla — lo que se detuvo fue poder cambiar el contenido.

La escala real del sistema

409pantallas
60clientes (tenants)
1,3req/s de API
~400conexiones WebSocket
53 GiBmedia · 33.402 archivos
2mercados · ES · AR

La carga es diminuta. Lo que se gana migrando es redundancia y disciplina operativa, no capacidad.

Antes y después

Antes — Donweb

2 VPS gestionadas con Laravel Forge · un solo proveedor

  • Una máquina por servicio. Sin réplica, sin failover.
  • Todo el media en disco local del VPS del static.
  • Los backups, en el mismo VPS que se cayó.
  • El panel se buildeaba dentro del VPS en cada deploy.
  • Sin infraestructura como código: restaurar era reconstruir a mano.
  • El parcheo de SO tumbó el parque medio día — autoinfligido, no del proveedor.
  • Caída de 2 días en agosto 2026: el disparador de todo esto.

Después — tres proveedores, por rol

Laravel Cloud (AWS) · DigitalOcean · Cloudflare

  • Sin parcheo de SO en el plano de aplicación: es PaaS.
  • 2 réplicas por servicio → deploys sin corte.
  • MySQL y Redis gestionados, con backups automáticos fuera de la máquina.
  • El media en object storage con CDN y egress cero.
  • Fronts en CDN, deploy solo en cada push.
  • Todo provisionado por scripts: reproducible, con verificación de drift.
  • Dos entornos: staging y producción, espejo uno del otro.

Por qué está repartida en tres proveedores

No es preferencia de herramientas. Cada pieza está donde está porque un límite medido la empuja ahí — y el disparador del proyecto fue justamente depender de un solo proveedor y de una sola máquina por servicio.

Laravel Cloud AWS

Los dos backends, la base y el caché

Qué resuelveEl peor incidente de la historia del sistema no lo causó el proveedor: lo causó el parcheo de SO de nuestra propia máquina. En PaaS ese incidente deja de existir.
  • MySQL y Redis gestionados, con backups fuera de la máquina.
  • Trae el pipeline de deploy, que antes no estaba versionado en ningún repo.
  • Región en la UE → GDPR, y ~25-40 ms a Madrid.

DigitalOcean

Reverb y ffmpeg, en máquinas propias

Por qué no en el PaaSDos bloqueos técnicos duros, no de precio.
  • Reverb: el WebSocket gestionado no acepta dominio propio, y ws.apiv3.signboox.com está horneado en la app de las 409 pantallas, que no tiene actualización remota. Cambiarlo es tocar el parque uno por uno.
  • ffmpeg: el PaaS no ofrece disco persistente a ningún precio y el disco temporal es la mitad de la RAM. Un video de 1 GB no entra.
  • Mismo datacenter que el PaaS → sin salto de internet en el medio.

Cloudflare

DNS, los archivos y los dos fronts

La razón económicaCon video, el egress es la línea de factura más grande e impredecible. R2 la pone en cero. En AWS + CloudFront, el mismo tráfico se factura por GB.
  • PoPs en Madrid y Buenos Aires: los dos mercados, servidos desde el borde.
  • El almacenamiento del PaaS es R2 revendido, pero no acepta dominio propio — y el dominio no se puede cambiar sin invalidar la caché de las 409 pantallas. En cuenta propia, además, sale 25% más barato.
  • Hosting de los dos fronts, gratis.

Costos

ConceptoUSD/mes
Laravel Cloud — producción
Plan Growth$20
API v3 — web (2 réplicas)$14
API v3 — Horizon$28
Static — web (2 réplicas)$14
Static — Horizon$14
MySQL gestionada + backups$18
Valkey ×2$12
Laravel Cloud — staging medido$55
DigitalOcean
Caja de Reverb$12
Caja de ffmpeg$24
Cloudflare · monitoreo
R2 — 53 GiB de media$1
Pages + DNS + CDN$0
Better Stack — monitoreo$0
Total plataforma nueva~$212

Staging es un tercio de la factura — y es lo que permite probar antes de tocar producción. Apagarlo entre releases baja el total a ~$157.

Cómo leer el númeroConfianza
Staging, gasto real de 7 días extrapoladomedido
DigitalOcean, precio públicofirme
Cloudflare R2, 45 GB facturables × $0,015firme
Producción en Laravel Cloud, precio de lista por recursode lista

Producción cuesta más que staging por tres cosas concretas, no por tamaño: no hiberna (staging duerme a los 5 min), lleva el doble de réplicas y su worker de colas es más grande.

El dimensionado no es un presupuesto inflado
El web de producción usa 1,2-1,4% de un vCPU. Lo que decide el tamaño es la estampida de 264 reconexiones tras un deploy — y ahí la palanca son réplicas, no RAM.
La base pesa 3,07 GB, de los cuales 3,0 son logs de dispositivo que nadie consulta. El working set caliente son ~40 MB.
El Redis de producción usa 3,3 MB, con pico de 7,7. Se contrató 250 MB: 32× el pico.
Tres tamaños bajaron respecto del plan original, después de medir las máquinas viejas.

Dónde estamos

PiezaStagingProducción nuevaDonweb
API v3 + Static✓ corriendo✓ desplegado, con dominio propio y HTTPS● vivo y congelado, atiende al parque
Los datos (60 tenants · 409 pantallas)✓ importados y verificados● fuente
Los archivos (53 GiB) en R2✓ 33.402 archivos, arqueo de bytes exacto● los sirve él
Reverb · ffmpeg✓ las dos cajas, verificadas desde internet● Reverb atiende al parque
Panel · Menu App✓ deploy en cada push✓ sirviendo la app real● siguen ahí

Lo que falta es el corte, y es DNS. La plataforma nueva está entera y se está ejercitando sobre un árbol de dominios propio (next.signboox.com). El día del cutover se apuntan los dominios finales y se apaga el tráfico a Donweb. No se borra nada en Donweb: queda como red de seguridad.