Capas y cache de build
Cada instrucción es una capa
Sección titulada «Cada instrucción es una capa»FROM node:20-alpine ← capa 1 (imagen base)WORKDIR /app ← capa 2COPY package.json . ← capa 3RUN npm ci ← capa 4COPY . . ← capa 5CMD ["node", "server.js"] ← metadata, no genera capa de filesystemCada RUN, COPY y ADD agrega una capa de solo lectura al filesystem de la imagen, apilada sobre la anterior. La imagen final es la suma de todas las capas.
El cache: por qué el segundo build es rápido
Sección titulada «El cache: por qué el segundo build es rápido»Docker cachea cada capa. Al reconstruir, compara cada instrucción con la ejecución anterior:
- Si la instrucción y lo que depende de ella (archivos copiados, instrucción anterior) no cambiaron → reusa la capa cacheada, sin volver a ejecutarla.
- Apenas una capa cambia, todas las capas siguientes se invalidan y se vuelven a ejecutar, aunque su contenido no haya cambiado.
Esto hace que el orden de las instrucciones importe muchísimo para la velocidad de build.
El patrón: dependencias antes que código
Sección titulada «El patrón: dependencias antes que código»FROM node:20-alpineWORKDIR /app
# 1. Copiar solo el manifiesto de dependenciasCOPY package.json package-lock.json ./RUN npm ci
# 2. Recién ahora copiar el resto del códigoCOPY . .
CMD ["node", "server.js"]Por qué funciona: el código de la aplicación cambia constantemente, pero las dependencias (package.json) cambian rara vez. Si COPY . . (todo el código) fuera antes de npm ci, cualquier cambio en una línea de código invalidaría también la capa de npm ci, reinstalando todas las dependencias en cada build aunque no haya cambiado ninguna.
Con el orden correcto: cambiar código → solo se reconstruyen las capas desde COPY . . en adelante; npm ci sigue cacheado.
Comparación
Sección titulada «Comparación»❌ Orden ingenuo ✅ Orden que aprovecha cacheCOPY . . COPY package.json ./RUN npm ci ← se re-ejecuta RUN npm ci ← cacheado COPY . .Forzar un build sin cache
Sección titulada «Forzar un build sin cache»docker build --no-cache -t mi-app .Útil para descartar que un problema sea “cache viejo” al debuggear, o para asegurar que una imagen se reconstruya 100% desde cero (por ejemplo, para traer parches de seguridad del sistema base).
Consideraciones
Sección titulada «Consideraciones»- El mismo principio aplica a cualquier lenguaje: copiar primero el manifiesto de dependencias (
requirements.txt,go.mod,Cargo.toml) e instalar, después copiar el resto del código. .dockerignoretambién afecta el cache — archivos que cambian todo el tiempo pero no deberían entrar a la imagen (comonode_moduleslocal o.git) invalidan cache innecesariamente si no están ignorados. Ver .dockerignore.