Arquitectura · 27 de octubre de 2026 · Daniel Murillo
Deploys en monorepos: problemas comunes y cómo resolverlos
Un monorepo no obliga a desplegar todo junto. Cómo detectar impacto, construir artefactos y publicar solo lo necesario.
Un monorepo puede contener muchas aplicaciones y librerías sin obligarlas a desplegarse juntas. El reto es construir, probar y publicar solo lo necesario, manteniendo correctas las dependencias entre proyectos.
Introducción
Un monorepo simplifica el trabajo con código compartido, pero puede complicar CI/CD si el pipeline trata todo el repositorio como una única aplicación.
Imagina un repositorio con una web, una API, un panel administrativo y varias librerías internas. Un cambio en el panel no debería obligar a reconstruir y desplegar la API si no existe una relación que lo justifique. Sin embargo, cambiar una librería compartida sí puede afectar a varios consumidores.
Los problemas más habituales son:
- Cada cambio dispara builds y despliegues innecesarios.
- Una aplicación no incluye sus dependencias internas al construir la imagen.
- El Docker build pierde caché porque copia archivos del repositorio demasiado pronto.
- Los filtros de CI no detectan cambios en librerías compartidas.
- Se publica una aplicación sin reconstruir los proyectos de los que depende.
- El artefacto que se probó no es exactamente el que se despliega.
- Las variables de entorno, migraciones y secretos se configuran de forma inconsistente entre servicios.
La solución no consiste en separar el repositorio automáticamente. Consiste en diseñar un pipeline que entienda las dependencias, genere artefactos reproducibles y mantenga independientes los ciclos de despliegue.
1. Primero: CI no es lo mismo que CD
CI (Continuous Integration) valida el cambio: instala dependencias, ejecuta lint, pruebas, compilaciones y otras verificaciones.
CD (Continuous Delivery/Deployment) prepara y entrega artefactos, publica imágenes o paquetes y los despliega en un entorno.
En un monorepo conviene distinguir al menos tres decisiones:
- Qué proyectos deben validarse por un cambio.
- Qué artefactos deben construirse y con qué dependencias.
- Qué aplicaciones deben desplegarse y a qué entornos.
No siempre los tres conjuntos son idénticos. Una librería compartida puede necesitar pruebas y builds para varios consumidores, pero el despliegue final podría limitarse a la API que la incorpora. Si la librería se publica como paquete independiente, su propia publicación también puede ser un paso separado.
2. Problema: todos los cambios ejecutan todo el pipeline
Una configuración ingenua ejecuta lint, tests, build y deploy para todas las aplicaciones en cada push.
Cambio en apps/admin
|
v
Build web + API + admin
|
v
Deploy web + API + admin
Esto es simple, pero desperdicia tiempo, minutos de CI y recursos de infraestructura. El problema se vuelve más evidente a medida que crece el número de proyectos.
Solución: detectar proyectos afectados
El pipeline debe conocer las relaciones entre proyectos y ejecutar las tareas relevantes para el cambio.
Con Nx, por ejemplo:
nx affected -t lint test build
Nx utiliza el grafo de proyectos y una base de comparación de Git para determinar qué proyectos están afectados. En CI, es importante que el checkout y la configuración de la base permitan comparar correctamente los commits.
La documentación oficial explica el flujo de CI y el uso de nx affected en Setting Up CI.
Si utilizas únicamente workspaces de Bun o pnpm, puedes empezar con filtros por paquetes y reglas de rutas. Sin embargo, las reglas de rutas por sí solas no entienden necesariamente el grafo completo de dependencias. Si apps/api depende de packages/contracts, un cambio en contracts debe activar las validaciones pertinentes de la API.
Regla importante: los filtros de CI deben respetar el grafo de dependencias, no solamente la carpeta modificada.
3. Problema: una aplicación depende de paquetes internos
Una aplicación puede compilar correctamente en la raíz del monorepo y fallar cuando intentamos desplegar solo su carpeta.
Ejemplo:
repo/
├── apps/
│ └── api/
└── packages/
├── contracts/
└── utils/
La API importa @acme/contracts y @acme/utils. Si copiamos únicamente apps/api a una imagen Docker, esos paquetes pueden no estar presentes.
Solución A: construir desde la raíz
El contexto de Docker debe incluir las dependencias internas necesarias. Por ejemplo:
docker build -f apps/api/Dockerfile .
El punto final indica que la raíz del repositorio es el contexto de build. El Dockerfile debe copiar e instalar los archivos necesarios para resolver los workspaces.
Es una estrategia directa, pero hay que evitar copiar indiscriminadamente todo el repositorio antes de instalar dependencias, porque eso puede invalidar la caché de Docker.
Solución B: generar un artefacto aislado
Otra opción es crear un directorio de despliegue que contenga la aplicación y las dependencias que necesita.
Con pnpm, pnpm deploy puede generar un directorio portable con las dependencias de producción del paquete, incluidas las dependencias de workspace que correspondan:
pnpm --filter=@acme/api --prod deploy ./deploy/api
Verifica la versión de pnpm y la configuración del workspace, ya que los detalles de deploy han evolucionado. Documentación: pnpm deploy.
Con Turborepo, turbo prune genera una versión reducida del monorepo con el código y lockfile relevantes para una aplicación:
turbo prune api --docker
turbo prune y pnpm deploy no producen exactamente lo mismo: uno genera un monorepo parcial para construir la aplicación; el otro prepara un paquete de despliegue aislado. La elección depende del flujo de build y runtime.
Referencias:
4. Problema: Docker invalida la caché con demasiada frecuencia
Un Dockerfile que copia todo el repositorio antes de instalar dependencias puede hacer que un cambio no relacionado invalide la capa de instalación.
COPY . .
RUN pnpm install
Si se modifica la web, la capa de instalación de la API puede reconstruirse aunque sus dependencias no hayan cambiado.
Solución: separar manifiestos y código fuente
La idea general es copiar primero los manifiestos y el lockfile necesarios, instalar dependencias y copiar después el código.
# Ejemplo conceptual: los COPY deben incluir todos los
# package.json y archivos de workspace requeridos por la app.
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY apps/api/package.json apps/api/package.json
COPY packages/contracts/package.json packages/contracts/package.json
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm --filter=@acme/api build
Este ejemplo ilustra el principio; en un monorepo real, los archivos package.json de todas las dependencias internas requeridas también deben estar presentes, y el layout de COPY debe respetar el workspace.
Para repositorios grandes, el pruning puede simplificar el proceso porque reduce los archivos que participan en el build. La guía de Turborepo explica cómo separar manifiestos y fuentes para mejorar el aprovechamiento de la caché de Docker: Docker guide.
5. Problema: desplegar solo el proyecto modificado rompe dependencias
Supongamos que apps/api depende de packages/contracts. Un cambio en contracts puede afectar la API aunque ningún archivo de apps/api haya cambiado.
Si el pipeline despliega solamente cuando cambia la carpeta apps/api, puede dejar producción con un artefacto que no corresponde al código actual o no ejecutar las pruebas necesarias.
Solución: definir la política de impacto
Para cada proyecto, define:
- Qué paquetes internos consume.
- Qué tareas deben ejecutarse cuando cambian esas dependencias.
- Qué artefactos se deben reconstruir.
- Qué aplicaciones deben volver a desplegarse.
- Qué cambios requieren validar todo el repositorio.
Una librería de tipos puede requerir pruebas y reconstrucción de sus consumidores. Una modificación exclusiva de documentación quizá no necesite un deploy. Un cambio en configuración raíz o lockfile puede afectar a muchos proyectos y merece una política explícita.
No todos los cambios del monorepo tienen el mismo alcance.
6. Problema: el pipeline detecta mal los cambios de Git
Las herramientas que calculan proyectos afectados necesitan una referencia de comparación correcta. Si el CI obtiene un checkout superficial o compara contra un commit incorrecto, puede no detectar los cambios esperados.
Solución
- Obtener el historial o las referencias que la herramienta necesita.
- Definir correctamente la rama base para pull requests.
- En pushes a la rama principal, comparar contra el commit base apropiado según el proveedor de CI.
- Probar el pipeline con cambios en una aplicación, una librería compartida, configuración raíz y lockfile.
- Revisar los logs de proyectos afectados cuando el resultado sea inesperado.
Nx documenta la importancia de la base de Git y de configurar el checkout para nx affected en Setting Up CI.
7. Problema: builds reproducibles y artefactos inconsistentes
Un riesgo común es construir una imagen en CI, probar una versión y después volver a construir en el momento del deploy. Si el entorno, las dependencias o las entradas cambian, lo desplegado podría no ser idéntico a lo validado.
Solución: construir una vez y promover el artefacto
Un pipeline más seguro suele seguir este flujo:
Commit / Pull Request
|
v
Lint + Tests + Build
|
v
Crear imagen con tag inmutable
|
v
Escanear y validar artefacto
|
v
Promover la misma imagen
|
v
Staging -> Producción
Utiliza lockfiles, instalaciones reproducibles, tags de imagen inmutables o digests y una política clara de promoción entre entornos. Evita depender de latest como identificador de una versión que debe poder reproducirse.
8. Problema: configuración y secretos mezclados
Las aplicaciones de un monorepo pueden tener variables de entorno distintas, permisos diferentes y ciclos de despliegue independientes. Compartir código no significa compartir secretos.
Solución
- Mantener secretos fuera del repositorio.
- Gestionar variables por aplicación y entorno.
- Aplicar permisos mínimos a cada servicio.
- Validar las variables obligatorias durante el arranque.
- Evitar que una configuración compartida introduzca dependencias accidentales entre aplicaciones.
- Documentar las diferencias entre desarrollo, staging y producción.
Una librería de configuración compartida puede centralizar validaciones y convenciones, pero no debería incluir credenciales reales ni asumir que todos los servicios usan las mismas variables.
9. Problema: migraciones de base de datos y despliegues coordinados
Una nueva versión de la API puede depender de un cambio de esquema. En un monorepo, el código de API y la migración pueden cambiar en el mismo commit, pero el despliegue no necesariamente es atómico.
Solución: migraciones compatibles
Cuando sea posible, utiliza una estrategia de expansión y contracción:
- Añadir el nuevo campo o estructura de forma compatible.
- Desplegar código que pueda funcionar con el esquema anterior y el nuevo.
- Migrar o rellenar datos.
- Cambiar los consumidores al nuevo esquema.
- Eliminar estructuras antiguas en una publicación posterior, cuando ya no se utilicen.
No todas las migraciones pueden hacerse sin coordinación, pero diseñar compatibilidad entre versiones reduce el riesgo de desplegar servicios en momentos distintos.
10. Un pipeline recomendado para monorepos
Una estrategia práctica puede dividirse en estas fases:
Pull request
- Instalar dependencias con lockfile reproducible.
- Detectar proyectos afectados.
- Ejecutar lint, pruebas y builds necesarios.
- Ejecutar pruebas de integración cuando cambien contratos o dependencias críticas.
- No desplegar producción desde cada pull request salvo que exista un flujo explícito para entornos efímeros.
Merge a la rama principal
- Recalcular el alcance del cambio.
- Construir artefactos de las aplicaciones afectadas.
- Crear imágenes inmutables.
- Ejecutar escaneos y validaciones.
- Publicar artefactos en un registro.
Despliegue
- Desplegar únicamente los servicios cuyo artefacto cambió o necesita actualizarse.
- Aplicar migraciones de forma segura.
- Ejecutar smoke tests y verificaciones de salud.
- Promover el mismo artefacto entre entornos.
- Tener una estrategia de rollback o roll-forward.
El diseño exacto depende del proveedor de CI/CD, el runtime y la plataforma de despliegue. GitHub Actions, GitLab CI, Azure DevOps, Jenkins y otros sistemas pueden implementar estos principios.
11. ¿Necesito Nx o Turborepo para resolverlo?
No siempre.
| Herramienta | Qué aporta | Cuándo alcanza |
|---|---|---|
| Bun o pnpm Workspaces | Paquetes y dependencias | Monorepo pequeño con scripts sencillos |
| Nx | Grafo, affected y caché de tareas | CI costosa y muchos proyectos relacionados |
| Turborepo | Orquestación y turbo prune para Docker | Quieres reducir el contexto de build por app |
| pnpm deploy | Paquete portable con deps de producción | Despliegue aislado desde un workspace |
Estas herramientas se solapan en algunas áreas, pero no son idénticas. La elección debe basarse en el grafo de dependencias, la estrategia de build, el formato del artefacto y la plataforma de despliegue.
Para la elección de Bun, pnpm y Nx en la capa de organización del código, ver Monorepo: Bun, pnpm y Nx.
12. Una regla importante: separar el repositorio del despliegue
Tener un único repositorio no obliga a desplegar todo junto. Puedes tener una única fuente de verdad para el código y mantener artefactos, imágenes, variables, escalado y ciclos de publicación separados.
La clave es distinguir:
| Unidad | Qué es |
|---|---|
| Unidad de código | El paquete o proyecto dentro del repositorio |
| Unidad de build | Fuentes y dependencias necesarias para producir un artefacto |
| Unidad de despliegue | La aplicación o servicio que se publica y opera de forma independiente |
Cuando estas unidades están definidas, el monorepo deja de ser una dificultad y se convierte en una forma de coordinar cambios sin renunciar a despliegues independientes.
Conclusión
Los problemas de despliegue en monorepos suelen venir de pipelines que no entienden el grafo de dependencias, builds que incluyen demasiado contexto, artefactos que no son reproducibles o políticas de despliegue demasiado amplias.
La solución es detectar el impacto real de cada cambio, construir solo lo necesario, incluir todas las dependencias internas, aprovechar correctamente la caché y promover artefactos inmutables.
Un monorepo no debe significar desplegar todo junto. Debe permitir cambiar código relacionado en un mismo lugar y desplegar cada aplicación con la independencia que necesita.
Empieza con un pipeline sencillo, define bien las dependencias y añade herramientas de orquestación cuando la complejidad y el coste de CI lo justifiquen.
En esta serie
- Monorepo: Bun, pnpm y Nx
- Deploys en monorepos (esta nota)