Cinco repositorios, un portátil con VirtualBox y cero minutos de CI en la nube
Un equipo partió su monorepo en cinco pipelines, agotó los minutos gratuitos de GitHub Actions y montó un runner autohospedado en un portátil con VirtualBox.

Separar un monorepo en cinco repositorios dio a cada componente su propio pipeline de integración continua. El aislamiento arquitectónico quedó perfecto. La factura, no: los 2.000 minutos gratuitos de GitHub Actions se agotaron en semanas porque cada commit mínimo disparaba cinco jobs independientes. En vez de comprar minutos, el equipo desplegó un runner autohospedado sobre un portátil de oficina con Windows, dentro del cual corre una máquina virtual Ubuntu con VirtualBox que atiende por turnos a los cinco proyectos.
La propia documentación de GitHub desaconseja enganchar runners locales a repositorios públicos: un pull request malicioso acaba ejecutando código en la máquina. Aquí los repositorios son privados, así que el riesgo externo estaba acotado. Aun así montaron higiene paranoica, y con razón: un solo runner procesa código de cinco proyectos distintos y los restos de un build anterior pueden arruinar el siguiente.
NAT, instantáneas y el flag --ephemeral
El sistema invitado sale por NAT (nic1=nat), sin acceso a la red local del anfitrión. Antes de cada job, un script devuelve la máquina virtual a una instantánea limpia. El runner arranca con el flag --ephemeral, que lo registra una sola vez, y un script en PowerShell, Unregister-OrphanedRunner.ps1, limpia por API los registros huérfanos que van quedando.
¿Por qué no usar los runners de organización que GitHub ya ofrece? Porque comparten el pool entre repositorios, pero no gestionan el ciclo de vida de las máquinas virtuales. El flag --ephemeral saca el runner del pool al terminar la tarea, y ahí se acaba su trabajo: no restaura el sistema de archivos ni reinicia nada. Para envolver cada build en un ciclo de rollback y arranque hacía falta un controlador externo que sondea la API REST. Ese script es el impuesto que se paga por tener un entorno limpio en cada compilación.
Un watchdog al que había que vigilar
Con un único servidor, el supervisor se convirtió en punto único de fallo de todo el CI. Las primeras semanas corrió sin vigilancia y pedía reinicio manual cada vez que VirtualBox dejaba procesos zombie. Añadieron un watchdog con lógica primitiva: si supervisor.log dejaba de actualizarse y VBoxHeadless no estaba en marcha, mataba el proceso y lo levantaba de nuevo.
El primer error fue de manual. El watchdog escribía sus propios diagnósticos en el mismo supervisor.log, así que actualizaba su mtime constantemente y bloqueaba su propia condición de disparo. Separaron los ficheros y pareció funcionar. A la mañana siguiente el watchdog empezó a matar a un supervisor perfectamente sano sin motivo aparente, lo que les costó dos días de investigación detrás de un fantasma de red que cuentan en la siguiente entrega.
El ahorro es real, pero el runner autohospedado no sale gratis: cambias la factura de minutos por trabajo de operación. Y el diseño tiene un límite conocido, porque todo el CI de cinco proyectos cuelga de un portátil de oficina y de un supervisor que hay que mantener vivo.