Optimizar una imagen Docker no significa perseguir el menor tamaño posible. Significa construir un runtime para el workload real.
mínimo, compatible, actualizable, seguro y operable
Texto
imagen óptima
= contenido necesario
+ base mantenida
+ dependencias compatibles
+ superficie reducida
+ diagnóstico posible
+ actualización controlada
Una imagen de 60 MB que falla con módulos nativos, no puede validar certificados o impide investigar incidentes es peor que una de 120 MB bien mantenida. El tamaño es una métrica; no es el objetivo completo.
docker image ls my-api
docker image inspect my-api --format'{{.Size}}'
El tamaño local puede incluir capas compartidas. No representa necesariamente el espacio adicional consumido ni el tamaño comprimido transferido desde el registry.
Una etapa de build puede incluir toolchain y source; la etapa final copia solo outputs.
docker
FROM node:22-bookworm AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:22-bookworm-slim AS runtime
ENV NODE_ENV=production
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev \
&& npm cache clean --force
COPY --from=build --chown=node:node /app/dist ./dist
USER node
CMD ["node", "dist/server.js"]
La etapa final no recibe:
TypeScript;
tests;
source original si no se copia;
compiladores;
cache de build;
dependencias de desarrollo.
Verifica que source maps, bundles y outputs no contengan secretos o rutas internas.
Revisa lifecycle scripts. Algunos paquetes compilan durante instalación y pueden requerir herramientas que no existen en la etapa final. Una estrategia es generar dependencias en una etapa compatible y copiarlas, pero módulos nativos exigen misma arquitectura y libc.
No elimines una dependencia solo porque parece “de desarrollo” si el runtime realmente la importa.
Menos paquetes pueden significar menos vulnerabilidades conocidas, pero el conteo bruto de CVEs puede engañar.
Evalúa:
severidad;
si el componente se usa o es alcanzable;
exposición del servicio;
exploit disponible;
fix disponible;
privilegios del proceso;
controles compensatorios.
Una base mínima pero abandonada es peor que una base algo mayor con mantenimiento activo.
Reconstruye periódicamente aunque el source no cambie para incorporar parches de base y dependencias. Cada rebuild debe pasar tests y despliegue gradual.
FROM runtime AS production
FROM runtime AS debug
USER root
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl procps \
&& rm -rf /var/lib/apt/lists/*
Publica tags claramente distintos y restringe el uso de la variante debug. No la conviertas en producción por comodidad.