π³ Docker - Advanced Image Optimization
Every byte in your Docker image is a liability. It takes longer to push, longer to pull, more storage cost, and potentially more security vulnerabilities. A Programmer treats the
Dockerfilelike production code, optimizing for Layer Caching and Minimal Runtimes.
ποΈ 1. Multi-Stage Builds
Section titled βποΈ 1. Multi-Stage BuildsβThis is the single most important pattern for creating production-ready images. It allows you to use heavy tools (compilers, build kits, node_modules) in a temporary βbuildβ container, and then copy only the final executable to a tiny βproductionβ container.
The Anatomy of a Multi-Stage Dockerfile:
Section titled βThe Anatomy of a Multi-Stage Dockerfile:β# Stage 1: BuildFROM node:20-slim AS builderWORKDIR /appCOPY package*.json ./RUN npm installCOPY . .RUN npm run build
# Stage 2: ProductionFROM nginx:alpine# We ONLY copy the static assets from the builder stageCOPY --from=builder /app/dist /usr/share/nginx/htmlEXPOSE 80CMD ["nginx", "-g", "daemon off;"]- Result: Your image size drops from ~1GB (Node + source + tools) to ~20MB (Nginx + static files).
β‘ 2. Optimizing Layer Caching
Section titled ββ‘ 2. Optimizing Layer CachingβDocker builds are cached by layers. If a layer hasnβt changed, Docker reuses it. If one layer changes, all subsequent layers must be rebuilt.
- The Junior Mistake:
COPY . .followed byRUN npm install. Any change to a single source file invalidates the cache for the entirenpm installstep. - The Senior Fix: Copy only the dependency files first.
COPY package.json package-lock.json ./RUN npm install # This layer is cached unless dependencies changeCOPY . . # Now copy the rest of the code
π 3. Choosing the Right Base Image
Section titled βπ 3. Choosing the Right Base ImageβStop using the default FROM node or FROM python. These are based on full Debian/Ubuntu distributions and contain hundreds of unnecessary packages.
-slim: A stripped-down version of the OS. Great balance between size and compatibility.alpine: Based on Alpine Linux. Tiny (~5MB base), but usesmuslinstead ofglibc, which can occasionally cause issues with specific C-extensions in Python or Node.distroless: Provided by Google. It contains only your application and its runtime dependencies. It doesnβt even have a shell (shorbash).- Programmer Benefit: If a hacker gains access to your container, they canβt run scripts because there is no shell to execute them.
π§Ή 4. Minimizing the Layer Count
Section titled βπ§Ή 4. Minimizing the Layer CountβEach RUN, COPY, and ADD command creates a new layer. While modern Docker versions are better at handling this, itβs still best practice to combine related commands.
- Anti-pattern:
RUN apt-get updateRUN apt-get install -y gitRUN rm -rf /var/lib/apt/lists/*
- Pattern:
Note: Cleaning up the cache in the same layer is vital. If you delete the cache in a separateRUN apt-get update && \apt-get install -y git && \rm -rf /var/lib/apt/lists/*
RUNcommand, it stays hidden in the previous layerβs history, still consuming space.
π‘οΈ 5. The .dockerignore File
Section titled βπ‘οΈ 5. The .dockerignore FileβJust like .gitignore, this prevents sensitive or heavy files from even being sent to the Docker daemon (the βbuild contextβ).
- Must-haves:
.git,node_modules,dist,.env,Dockerfile, and local logs. - Why? A smaller build context means faster builds and prevents accidentally baking secrets into your image layers.
π 6. Security & Best Practices
Section titled βπ 6. Security & Best Practicesβ- Non-Root User: By default, Docker runs as
root. If your container is compromised, the attacker has root access. Always create and use a dedicated user.RUN useradd -m myuserUSER myuser - Scan your Images: Use tools like Snyk or Trivy to scan your final images for known vulnerabilities (CVEs) in your dependencies or base OS.
π‘ Note: Use Dive (
wagoodman/dive). Itβs a CLI tool that lets you inspect a Docker image layer-by-layer to see exactly what files were added in each step and where you are wasting space.