Sachith Dassanayake Software Engineering Dockerfile hygiene & multi‑stage builds — Performance Tuning Guide — Practical Guide (Dec 20, 2025)

Dockerfile hygiene & multi‑stage builds — Performance Tuning Guide — Practical Guide (Dec 20, 2025)

Dockerfile hygiene & multi‑stage builds — Performance Tuning Guide — Practical Guide (Dec 20, 2025)

Dockerfile hygiene & multi‑stage builds — Performance Tuning Guide

Level: Intermediate

Date: December 20, 2025

Docker remains a cornerstone for containerising applications, and in 2025 its core features have stabilised around versions 24.x.x. Optimising your Dockerfiles not only improves build speed and image size but also enhances runtime performance and security. This guide explores practical principles for Dockerfile hygiene and multi-stage builds, focusing on best practices relevant for Docker CE 20.10+ through the latest stable releases of 24.x. We’ll steer you away from common pitfalls and help you validate your optimisations effectively.

Prerequisites

  • Basic knowledge of Docker CLI and Dockerfile syntax.
  • Docker Engine version 20.10 or later (includes BuildKit enabled by default).
  • Familiarity with container image concepts such as layers and manifests.
  • Access to Docker Hub or equivalent registry for pulling base images.

Hands-on steps

1. Understand Dockerfile hygiene

Maintaining a clean, efficient Dockerfile is crucial for build performance and image size. Here are key tactics:

  • Minimise layers: Combine related commands with && to reduce the overall layer count. Each RUN, COPY, and ADD creates a new layer.
  • Order your instructions: Place less frequently changing instructions near the top to leverage build cache better. For example, install dependencies before copying your application code.
  • Avoid unnecessary tools in final images: Limit build-time tools and debugging utilities to intermediate stages.
  • Clean up in the same layer: Remove package caches and temporary files inside the same RUN command where you install or generate them.
FROM python:3.11-slim AS builder

RUN apt-get update && apt-get install -y build-essential 
  && pip install --no-cache-dir wheel setuptools && 
  apt-get clean && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY requirements.txt .
RUN pip wheel -r requirements.txt --wheel-dir=wheels

FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /app/wheels /wheels
RUN pip install --no-cache-dir /wheels/*

COPY . .
CMD ["python", "app.py"]

2. Use multi-stage builds

Introduced officially since Docker 17.05, multi-stage builds let you separate build-time dependencies from runtime images, significantly reducing image size and attack surface. By copying only necessary artefacts from build stages, you keep your final image lean.

  • Use descriptive stage names for clarity (AS builder, AS tester, etc.).
  • Minimise copy operations to only required artefacts.
  • Choose minimal base images for the final stage, such as alpine, distroless (Google Distroless images are stable and recommended for security-conscious workloads), or slim variants.

3. Optimise caching and build context

Build cache is key to performance. Ensure that:

  • Files are copied strategically. Copy files that rarely change before volatile ones. This increases the likelihood of cache hits.
  • .dockerignore is well-maintained: Exclude unnecessary files (logs, local environment configs, node_modules, etc.) from the build context to reduce upload time and cache invalidations.
# .dockerignore example
.git
node_modules
*.log
.env
.vscode

4. Leverage BuildKit features

Since Docker 18.09, BuildKit is stable and the default builder in Docker 24.x. It dramatically improves build parallelism and caching. Some tips:

  • Use --mount=type=cache for persistent cache between builds (e.g., for package managers or language-specific caches).
  • Use RUN --mount=type=secret for injecting sensitive build-time secrets (like API keys) without retaining them in final images.
  • Enable BuildKit explicitly if needed with DOCKER_BUILDKIT=1 (usually not necessary on recent Docker).
FROM node:20-alpine AS builder
WORKDIR /app

RUN --mount=type=cache,target=/root/.npm 
    npm install

COPY . .
RUN npm run build

Common pitfalls

  • Too many layers from RUN instructions: Fragmented installs cause extra overhead and slower builds.
  • Copying large directories unnecessarily: Leads to slower build context uploads and cache invalidation.
  • No .dockerignore or poorly maintained .dockerignore: Causes large contexts which slow builds.
  • Installing build tools in final image: Results in larger attack surface and unnecessary image weight.
  • Ignoring cache implications when changing Dockerfile order: Changing order can invalidate cache leading to longer builds.
  • Overusing ADD instead of COPY: Use COPY unless leveraging ADD for tar extraction or URL fetches (which have their own risks).

Validation

To confirm that your Dockerfile improvements yield genuine benefits:

  • Measure build time: Use time docker build . or automated CI build logs.
  • Check image size: Use docker image inspect or docker images and look at SIZE column.
  • Examine layers: Use docker history <image> to see layer size and changes. Tools like container-diff aid analysis.
  • Scan for security: Scan your final images with tools like Docker Scan (powered by Snyk) to detect bloat or vulnerabilities.

Checklist / TL;DR

  • Use multi-stage builds to isolate build dependencies from runtime environment.
  • Write concise Dockerfiles with minimal layers by combining commands sensibly.
  • Order instructions to maximise caching on rarely changing steps.
  • Maintain a comprehensive .dockerignore to reduce build context size.
  • Enable and leverage BuildKit features for caching, secrets, and mounts.
  • Avoid copy of unnecessary files or debugging tools in the final image.
  • Validate improvements by measuring build time and image size before/after.
  • Scan final images for security and remove unneeded components.

References

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Related Post