🐳 Docker file multipaso
Hasta ahora hemos construido imágenes Docker copiando todo el proyecto dentro del contenedor.
Sin embargo, muchas aplicaciones modernas necesitan un proceso de compilación antes de poder ejecutarse.
En estos casos no tiene sentido incluir en la imagen final:
- El código fuente.
- Las dependencias de desarrollo.
- Los archivos utilizados únicamente durante la compilación.
Para solucionar este problema existen los Dockerfiles multipaso (Multi-stage Builds), que permiten dividir la construcción de la imagen en distintas etapas.
🏗️ El problema de una imagen tradicional
Una aplicación puede requerir ejecutar un proceso de build que:
- Resuelva dependencias.
- Compile el código.
- Genere un bundle optimizado.
Si copiamos directamente todo el proyecto dentro de la imagen Docker, también estaremos incluyendo archivos que nunca serán utilizados en producción.
Esto provoca imágenes:
- Más grandes.
- Más lentas de transferir.
- Con mayor superficie de ataque.
- Con archivos innecesarios para ejecutar la aplicación.
🚀 Primera etapa: el Builder
La primera fase del Dockerfile se dedica exclusivamente a construir la aplicación.
Se define una imagen base y se le asigna un nombre utilizando AS:
FROM node:alpine AS builder
Dentro de esta etapa se realizan las tareas habituales:
- Copiar el
package.json. - Instalar dependencias.
- Copiar el código fuente.
- Ejecutar la compilación.
Finalmente se ejecuta:
npm run build
Esta etapa únicamente existe para generar la versión compilada de la aplicación.
📦 La compilación genera una carpeta optimizada
Tras ejecutar la build se genera una carpeta como dist, que contiene la aplicación preparada para ejecutarse.
Durante este proceso:
- Se resuelven las dependencias.
- Se empaqueta el código.
- Se eliminan imports innecesarios.
- Se produce un único resultado listo para producción.
A partir de este momento ya no es necesario conservar el código fuente original dentro de la imagen.
🏃 Segunda etapa: el Runner
La segunda etapa representa la imagen que realmente llegará a producción.
También parte de una imagen base:
FROM node:alpine AS runner
En esta fase:
- Se configura el entorno.
- Se establecen variables de entorno.
- Se copian únicamente los archivos compilados.
- Se expone el puerto correspondiente.
- Se define el comando de ejecución.
Lo importante es que esta etapa no recompila nada.
Simplemente utiliza el resultado generado por el builder.
📁 Copiando archivos desde otra etapa
Docker permite copiar archivos generados en una etapa anterior mediante:
COPY --from=builder /app/dist ./dist
Gracias a esto únicamente se incorpora al contenedor final la carpeta compilada.
Todo el resto del proyecto desaparece de la imagen definitiva.
🌎 Configurar correctamente el entorno
Durante la etapa de ejecución es recomendable establecer:
ENV NODE_ENV=production
De esta forma la aplicación se ejecutará en modo producción.
Esto evita comportamientos propios del entorno de desarrollo, como:
- Comprobaciones adicionales.
- Herramientas de depuración.
- Código pensado únicamente para desarrollo.
El resultado es un mejor rendimiento de la aplicación.
✨ Un contenedor mucho más limpio
La principal ventaja del enfoque multipaso es que la imagen final únicamente contiene lo imprescindible.
En lugar de incluir:
- Código fuente.
node_modules.package.json.- Archivos de desarrollo.
La imagen únicamente conserva:
- La carpeta
dist. - El archivo compilado listo para ejecutarse.
Esto hace que el contenedor sea mucho más sencillo y seguro.
🔍 Comprobando el resultado
Al iniciar un contenedor construido mediante un Dockerfile multipaso puede comprobarse que dentro del directorio de trabajo solo existe la carpeta compilada.
No aparecen:
- Dependencias de desarrollo.
- Código original.
- Archivos del proyecto.
Únicamente permanece el resultado generado durante la etapa de build.
📉 Imágenes más pequeñas
Aunque en el ejemplo la diferencia de tamaño ronda únicamente algunos megabytes, el beneficio aumenta considerablemente en proyectos reales.
Reducir el tamaño de una imagen implica:
- Menor espacio de almacenamiento.
- Menor tiempo de descarga.
- Menor tiempo de despliegue.
- Menor transferencia entre servidores.
En aplicaciones grandes el ahorro puede ser muy significativo.
📌 Ideas clave de esta clase
Quédate con estos conceptos:
- Un Dockerfile puede tener varias etapas.
- La primera etapa se utiliza para compilar la aplicación.
- La segunda etapa únicamente ejecuta el resultado compilado.
AS builderpermite nombrar una etapa.COPY --from=buildercopia archivos desde otra fase.- Es recomendable configurar
NODE_ENV=production. - La imagen final contiene únicamente lo necesario para ejecutarse.
- Las imágenes multipaso son más limpias, ligeras y seguras.
🚀 Lo siguiente
Ahora que ya conoces los Dockerfiles multipaso, puedes construir imágenes optimizadas para producción eliminando todo aquello que solo es necesario durante la compilación.
Este patrón se ha convertido en una de las mejores prácticas más utilizadas en proyectos Docker modernos.
💡 Tip: Siempre que tu aplicación requiera un proceso de compilación, utiliza un Dockerfile multipaso. Mantendrás imágenes más pequeñas, reducirás tiempos de despliegue y evitarás incluir archivos innecesarios en producción.