Docker build cache follows the order of your Dockerfile. Put rarely changing dependency files before frequently changing app files so edits to source code do not force dependency installation every time.
Multi-stage builds let one stage compile or install heavy tooling, while the final stage copies only the runtime artifacts. The result is usually smaller, faster to ship, and less exposed to unnecessary packages.
Cache-friendly order
Put dependency manifests before source files. For Node this usually means copying package files first, installing dependencies, and only then copying the rest of the app.
For Python, copy the lockfile or requirements first, install, then copy source. The pattern is the same even when the ecosystem changes.
COPY package.json package-lock.json ./
RUN npm ci
COPY . .Multi-stage builds
A builder stage can contain compilers, package caches, and build tools. The final stage should contain only what the app needs at runtime.
This makes images smaller and removes tools attackers do not need to find inside production containers.
FROM node:22-alpine AS build
RUN npm ci && npm run build
FROM node:22-alpine
COPY --from=build /app/build ./buildWhen cache lies to you
Cache is a speed feature, not a correctness guarantee. If a build result looks impossible, rebuild once with no cache before spending time debugging app code.
docker build --no-cache -t app-debug .what to remember
- Copy lockfiles before app source when installing dependencies.
- Use a builder stage for compilers, package managers, and generated artifacts.
- If a build behaves strangely, rebuild without cache once before chasing app bugs.