Índice del diarioDockup / nota de campo
Note / build-fails-with-no-logs

Build fallido sin logs: cómo obtener la salida

Que un build falle sin logs significa que el fallo ocurrió antes de que comenzara el build. Descubre las cuatro fases en las que puede suceder, cómo diferenciarlas y cómo obtener la salida de cada una.

«Build failed». Sin stack trace, sin errores del compilador, sin ninguna salida. Un build fallido sin logs es el mensaje menos útil que puede producir una plataforma y normalmente significa algo concreto que conviene entender: el fallo ocurrió antes de que comenzara aquello que genera los logs.

Un build no es un único paso. Son cuatro, y cada uno falla de una manera distinta.

Las cuatro fases

1. Obtener el código fuente. La plataforma clona tu repositorio en una ref. 2. Preparar el build. Determina cómo hacer el build: Dockerfile, buildpack o un framework detectado. 3. Ejecutar el build. Se ejecutan tus comandos. Esta es la única fase que produce la salida que esperas. 4. Crear el paquete. El resultado se convierte en una imagen ejecutable.

Si no tienes ningún log, el fallo ocurrió en la fase 1 o 2. Tu build nunca llegó a ejecutarse, así que no pudo imprimir nada.

Fase 1: nunca obtuvo tu código

Los síntomas son silencio total y un fallo rápido, normalmente en menos de quince segundos.

Causas habituales, por orden:

  • La rama no existe. Un servicio configurado para desplegar master en un repositorio que cambió el nombre a main. Falla al instante y apenas muestra información.
  • Se revocó el acceso. Se eliminó el token o la instalación de la app que funcionaba el mes pasado, o el repositorio se trasladó a una organización donde el permiso ya no se aplica.
  • El repositorio es privado y la conexión caducó. Tiene el mismo patrón que el caso anterior: la plataforma recibe un 404 en lugar de un 403, porque eso es lo que devuelven los proveedores de Git para los repositorios privados que no puedes ver.
  • No se puede obtener un submódulo. El repositorio principal se clona, pero falla un submódulo que usa una URL SSH porque el entorno de build no tiene una clave para acceder a él.

La comprobación rápida: ¿la plataforma muestra un hash de commit para el despliegue fallido? Si no lo muestra, nunca obtuvo el código y nada de tu Dockerfile es relevante.

Fase 2: no sabe cómo hacer el build

También es silenciosa, porque todavía no se ha elegido ningún comando de build.

  • No hay ningún Dockerfile donde indica la configuración. Un dockerfilePath apunta a una ruta que se ha movido.
  • Un monorepo sin raíz configurada. La plataforma está mirando la raíz del repositorio y tu servicio se encuentra en apps/api.
  • La detección no encontró nada. No hay ningún manifest reconocido, por lo que no coincidió ningún buildpack.
  • Un Dockerfile no se puede analizar. Un error de sintaxis en la línea 1 provoca el fallo antes de que se ejecute ninguna layer.

Fase 3: aquí es donde existen los logs

Si ves una salida parcial que se detiene de repente, estás en la fase 3, y las dos causas más habituales están relacionadas con los recursos, no con el código:

Memoria insuficiente. Un build terminado por el OOM reaper no tiene ocasión de imprimir nada al respecto. El log simplemente se detiene a mitad de un paso. Los builds de TypeScript, webpack y Vite en codebases grandes alcanzan este límite con frecuencia, y la pista es que el mismo commit funciona en tu portátil, que tiene más memoria que el builder.

Timeout. Un build que supera el límite de la plataforma se termina. El síntoma es el mismo: la salida se detiene en lugar de finalizar.

Ambos casos parecen «sin logs» si el fallo ocurre lo bastante pronto.

Fase 4: el build terminó, pero no se puede empaquetar

Es poco frecuente y bastante específico: el build terminó correctamente, pero el artefacto es incorrecto. Puede tratarse de una imagen sin CMD o ENTRYPOINT, una incompatibilidad de arquitectura o una imagen demasiado grande para el límite de la plataforma.

Orden de diagnóstico

# Is there a commit hash? If not, stage 1.
dockup deployments my-project/my-api --json

# Build logs of the latest deployment, streamed as it goes
dockup logs my-project/my-api --build --follow

# The full record, including which stage took how long
dockup status my-project/my-api --json

stageTimings en esa última salida es la forma más rápida de localizar el fallo. Un despliegue que tardó 0,4 segundos en clonar y terminó con un fallo en la fase 1. Uno que pasó noventa segundos haciendo el build y después se detuvo es un problema de la fase 3, probablemente de memoria.

Cómo obtener la salida cuando no hay ninguna

Tres técnicas, en orden de esfuerzo:

Reproduce la limitación localmente. No se trata de «¿funciona el build en mi máquina?», sino de hacerlo con la misma memoria que tiene el builder:

docker build --memory=2g --memory-swap=2g -t test .

Si así reproduces el fallo, lo has encontrado: es un problema de memoria, no algo misterioso.

Haz que tu build sea más verboso. La mayoría de las build tools son silenciosas por defecto sobre aquello que está a punto de terminarlas.

# Print progress so a truncated log still shows where it stopped
RUN npm ci --loglevel verbose
RUN NODE_OPTIONS="--max-old-space-size=3072" npm run build

Vale la pena probar esa línea de NODE_OPTIONS por sí sola: un build de Node que muere silenciosamente suele estar limitado por el heap, y aumentarlo soluciona builds que no produjeron ningún diagnóstico.

Haz una búsqueda binaria en el Dockerfile. Comenta todo lo que haya después del paso que falla y añade marcadores RUN echo "reached step N". Es rudimentario, pero funciona cuando nada más lo hace.

Qué reduce este tipo de problemas

Hay dos cosas más importantes que cualquier técnica de depuración.

Logs en streaming en lugar de logs resumidos. Si la salida solo aparece después de que termine el build, un build terminado por el sistema no produce nada, porque el resumen se escribe al final. El streaming garantiza que el log disponible cuando muere contiene todo hasta el momento del fallo.

dockup logs my-project/my-api --build --follow

Fases con nombre y duración. «Build failed» es un único bit de información. «Clone: 0.4s, build: failed after 94s» basta para descartar tres de las cuatro causas anteriores sin leer nada más.

Preguntas frecuentes

¿Por qué mi build no produce ningún log? Porque falló antes de que se ejecutaran tus comandos de build, normalmente al obtener el código fuente o al determinar cómo hacer el build. Ninguna de esas fases produce salida del build.

¿Por qué funciona en local, pero no en la plataforma? Lo más habitual es que sea un problema de memoria. Tu máquina tiene más memoria que el builder. Reproduce el proceso con docker build --memory=2g para confirmarlo antes de investigar otra cosa.

¿Qué significa que un log se detenga a mitad de un paso? El proceso terminó de forma forzada en lugar de salir normalmente. Las dos posibilidades principales son memoria insuficiente o un timeout del build, y el OOM killer no da al proceso la oportunidad de explicarse.

¿Necesito un Dockerfile? No necesariamente: las plataformas pueden detectar tipos de proyecto habituales y hacer el build sin uno. Sin embargo, si la detección falla, se produce un fallo silencioso y sin logs, por lo que un Dockerfile explícito elimina toda una categoría de ambigüedades.