Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To speed up Docker builds, arrange instructions so stable inputs—such as the base image and dependency manifests—come before frequently changing source code. Keep the build context small with a .dockerignore file, use BuildKit cache mounts for expensive package or compiler work, and copy only runtime artifacts into a final stage. The key is preserving useful cache hits: when a layer misses, Docker reruns that instruction and the instructions after it.

How Dockerfile order affects build time

Docker evaluates instructions in order and can reuse cached results when an instruction and its relevant inputs match. A cache miss causes that instruction and all subsequent instructions to run again. As Docker puts it, “If a layer changes, all other layers that come after it are also affected.” Docker build cache

That makes instruction order important. If a Dockerfile copies the whole repository before installing dependencies, a source edit can invalidate the copy layer and force the dependency-install step to run again. Copying dependency manifests first and application source later lets a source-only change leave the earlier dependency layer reusable.

Docker recommends ordering instructions from less frequently changed to more frequently changed where possible. Build cache invalidation

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A practical Dockerfile pattern

This Node.js example separates dependency installation, application build, and runtime assembly. It is a pattern to adapt—not a universal performance guarantee.

# syntax=docker/dockerfile:1
FROM node:22-alpine AS build
WORKDIR /app

# Stable dependency inputs first
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm npm ci

# Frequently changing source later
COPY . .
RUN npm run build

FROM node:22-alpine AS runtime
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
CMD ["node", "dist/server.js"]
  1. Start with a stable base and working directory. Changes to the base image can invalidate later work, so use a deliberate base-image choice and pin inputs when reproducibility matters.
  2. Copy only dependency manifests. For Node.js, that means the package manifest and lockfile; use the equivalent manifest and lockfile for your ecosystem.
  3. Install dependencies with a cache mount. The mount can retain package downloads between builds without baking that cache into an image layer.
  4. Copy the application source after dependency installation. This isolates frequent source edits from the dependency layer, provided the manifests themselves have not changed.
  5. Run tests or compilation, then assemble the runtime stage. Copy only files needed to run the application into that stage.

Keep the build context small

The build context is the set of files made available to the builder. Files that are irrelevant to the image can increase transfer work and create unnecessary cache inputs. Add a .dockerignore file at the root of the build context and exclude items the build does not need.

  • .git history
  • Local dependency directories, such as node_modules
  • Logs, test reports, and editor settings
  • Generated artifacts that the Dockerfile rebuilds itself

Review the exclusions when the Dockerfile begins copying a new required file. Docker documents .dockerignore files as a way to reduce what is sent as build context.

Enable BuildKit and use cache mounts selectively

BuildKit is Docker’s builder backend. Its graph solver can run independent steps concurrently; it can also transfer only changed context data, skip unused stages, and manage cache more effectively than the legacy builder. Docker describes it as improving build functionality and performance over the legacy builder. BuildKit

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The example’s RUN --mount=type=cache is a BuildKit feature. Set target to the cache directory used by the package manager or compiler in your image. The Node.js example uses /root/.npm; another toolchain may need a different path. Choose sharing options with the tool’s behavior in mind, and check its documentation for the correct cache directory and concurrency expectations. Cache-mount syntax and options are described in the Dockerfile reference.

A cache mount is not an image layer: it preserves reusable downloads or intermediate cache data across builds without adding that cache to the final image. It does not replace the ordinary layer cache. If the dependency-install instruction itself is invalidated, the mount may still help avoid downloading data again.

Use multi-stage builds for a lean runtime

A multi-stage Dockerfile starts a new stage with another FROM and copies selected output from an earlier stage. This keeps compilers and intermediate files out of the runtime image, and independent stages may allow parallel work. Docker explains the pattern in its multi-stage build guide.

Multi-stage builds do not automatically make every compile step faster. Their build-time benefit depends on whether they help reuse cached work or run independent steps concurrently. For repeated builds, preserving cache hits and avoiding irrelevant context changes are usually the more direct optimizations.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Know what invalidates the cache

Docker compares instructions and their relevant inputs with cached layers. For COPY and ADD, as well as bind-mounted RUN steps, file metadata contributes to the cache checksum; modification time alone does not invalidate the cache. A changed command, base image, or copied file can cause a cache miss, and later instructions then run again. See Docker’s cache invalidation guidance.

  • Keep dependency manifests in their own copy step before volatile source files.
  • Avoid copying the entire repository before dependency installation when source changes frequently.
  • Expect dependency installation to run again when its manifest or lockfile changes; that is generally necessary to reflect the new inputs.
  • Keep secrets out of ordinary ARG or ENV values; use the supported secret-mount mechanisms documented for your builder.

Measure whether a change helped

There is no single percentage improvement that applies to all projects. Results depend on the project’s dependency graph, language toolchain, storage, network, and CI setup. Compare builds under consistent conditions instead of assuming a Dockerfile pattern guarantees a speedup.

  • Cache-hit rate after changing application source only
  • Cold build time and warm build time
  • Build-context transfer size
  • Dependency-download volume
  • Final image size
  • Reproducibility of pinned inputs
  • Maintenance complexity introduced by cache mounts and multiple stages

If a source-only edit still reruns dependency installation, inspect the Dockerfile order and the files included in the dependency copy step. If the context transfer is unexpectedly large, review .dockerignore. If downloads dominate otherwise-cached builds, check that the cache mount targets the package manager’s actual cache directory.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.