A Docker build that once took two minutes can suddenly take much longer, even when the application code has barely changed. Developers often blame Docker, but slow builds can result from storage delays, poor cache usage, memory pressure, or unnecessary file operations. Understanding where the time goes is the first step toward fixing the problem.

What Actually Makes a Docker Build Slow?

Docker builds can slow down when the host system cannot process build operations efficiently.

Building a container image involves reading source files, downloading dependencies, extracting packages, compiling code, and creating image layers. These operations place different demands on CPU, memory, storage, and network resources.

For example, compiling a large application can be CPU-intensive, while installing thousands of small dependencies can involve frequent filesystem operations.

A build might also spend considerable time downloading packages because of network latency or remote registry performance.

This means a slow build does not automatically indicate a slow SSD or overloaded processor.

Before changing hardware, identify which stage consumes the most time and which system resource is limiting it.

Identify the Slowest Docker Build Stage

The fastest way to investigate is to examine the build output.

Run the following command from the directory containing your Dockerfile:

docker buildx build --progress=plain .

The --progress=plain option provides detailed build output, making it easier to identify slow instructions.

Pay attention to operations such as dependency installation, source compilation, copying files, and extracting image layers.

If the build spends most of its time downloading packages, network performance may be the problem.

If dependency installation or compilation takes unusually long, CPU, storage, and cache behavior deserve closer attention.

Compare the first build with a second build using the same Dockerfile and unchanged source files.

If the second build is much faster, the build cache is probably helping. If both builds remain slow, inspect the individual stages and host resources.

Docker's build cache documentation explains how image layers are reused and when they must be rebuilt.

Check Linux Disk I/O Performance

Linux provides tools for checking whether storage activity is contributing to poor performance.

One useful utility is iostat, which is included in the sysstat package.

On the Linux host running the Docker builder, execute:

iostat -xz 1 10

This displays extended disk statistics at one-second intervals.

Several metrics are particularly useful when troubleshooting.

Understanding Disk Latency

The await value reports the average time, in milliseconds, required to complete I/O requests, including time spent waiting in a queue.

A higher value during a slow build can indicate storage delays, particularly when compared with the same system under normal conditions.

However, there is no universal acceptable await value. Different storage devices and workloads have different performance characteristics.

The r_await and w_await values help distinguish read latency from write latency.

The %util metric shows the percentage of elapsed time during which the device had I/O activity.

On traditional disks handling requests serially, sustained values near 100% can indicate saturation.

On modern SSDs and parallel storage systems, %util alone does not reliably indicate maximum device capacity.

The important principle is to examine several metrics together rather than relying on one number.

For additional details, refer to the official Linux iostat documentation.

Find Which Processes Are Generating Disk Activity

Once disk activity appears suspicious, identify the processes responsible.

Use the following command:

pidstat -d 1 10

This reports disk I/O activity for active processes over a series of one-second intervals.

The output includes read and write rates, which can help identify processes generating substantial storage traffic.

On a shared development server, Docker may be competing with databases, backup jobs, monitoring applications, or other build processes.

A process generating large amounts of I/O is not automatically the cause of a slowdown. High throughput can be normal when a storage device is operating efficiently.

Look for a relationship between process activity, increasing disk latency, and slower build stages.

Also remember that some Docker build operations may execute through separate builder services or processes.

Monitoring the actual Linux host is important because container-level measurements may not reveal the complete storage workload.

Check Whether Memory Pressure Is Slowing the Build

Insufficient available memory can create additional storage pressure.

When Linux needs more memory, it may reclaim cached pages or, where configured, move memory pages into swap space.

Excessive swapping can make applications and build operations noticeably slower.

Run:

vmstat 1 10

The si and so columns report swap-in and swap-out activity.

Sustained swapping during a slow build suggests memory pressure deserves investigation.

The wa column reports CPU time classified as waiting for I/O. However, high I/O wait is not proof that the physical storage device is defective or saturated.

Check memory usage, system load, and storage latency together.

If memory pressure is the primary issue, adding faster storage may not solve the underlying problem.

Similarly, increasing RAM will not fix a Dockerfile that repeatedly downloads unnecessary dependencies.

Understand How Docker Storage Affects Performance

Docker stores image data using layers and manages filesystem changes through its configured storage backend.

In environments using OverlayFS, modifying a file from a lower image layer can trigger a copy-up operation.

This may introduce additional overhead, particularly for large files.

The storage implementation also matters when applications write heavily to container filesystems.

Docker generally recommends volumes for persistent, write-intensive application data instead of relying on the container's writable layer.

However, Docker build performance and application runtime storage performance are separate issues.

A database running inside a container and a Docker build compiling an application may require different optimizations.

For modern Docker Engine installations, storage behavior also depends on whether the system uses the containerd image store or a classic storage driver.

Avoid assuming every Docker installation has the same storage configuration.

The official Docker storage documentation explains these differences and their practical implications.

Improve Dockerfile Layer Caching

One of the most effective ways to reduce build time is to avoid repeating expensive operations unnecessarily.

Consider a Node.js project.

If the Dockerfile copies the entire project before installing dependencies, a small source code change may invalidate the dependency installation layer.

This forces Docker to execute the installation step again.

A better approach is to copy the dependency manifests first.

For example, a typical Node.js Dockerfile can use these instructions in sequence:

COPY package.json package-lock.json ./

RUN npm ci

COPY . .

With this structure, Docker can reuse the dependency installation layer when the dependency files have not changed.

Changing application source code does not automatically require reinstalling every dependency.

This technique can make repeated development builds more efficient without any hardware upgrade.

Use Cache Mounts for Package Managers

BuildKit also supports cache mounts that preserve reusable package data between builds.

For a Node.js application, the installation instruction can be written as:

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

The cache mount allows downloaded npm package data to remain available for subsequent builds using the same builder cache.

Even when the installation layer must be rebuilt, the package manager may reuse previously downloaded resources.

Cache mounts are not a replacement for correct dependency management. Their availability also depends on the builder configuration and cache lifecycle.

For CI pipelines that create a fresh builder for every job, consider Docker's external build cache mechanisms.

The Docker cache optimization guide provides supported examples.

Reduce Unnecessary Build Context Files

Docker builds also depend on the files supplied as build context.

A project containing large dependency directories, temporary files, test results, and generated assets may send more data to the builder than necessary.

Use a .dockerignore file to exclude files that are not needed during the build.

For a typical Node.js application, this may include local dependency directories, temporary logs, and unnecessary development artifacts.

Be careful not to exclude source files or configuration files required by the image.

Reducing the build context can improve efficiency, especially when the builder operates remotely.

It can also reduce unnecessary cache invalidation when unrelated files change.

When Does Faster Storage Actually Help?

Upgrading storage makes sense when measurements show that storage performance is limiting the workload.

Traditional hard drives generally offer lower random I/O performance than SSDs, making SSDs more suitable for many workloads involving frequent small reads and writes.

NVMe SSDs can provide higher throughput and more concurrent I/O capability than SATA SSDs, although real performance depends on the model, platform, and workload.

For Docker builds involving substantial file extraction, dependency installation, or intermediate file creation, faster storage may reduce waiting time.

However, the difference may be small when the build is limited by CPU performance, network downloads, or ineffective caching.

Enterprise storage selection should therefore consider workload characteristics rather than interface speed alone.

Before approving a hardware upgrade, compare build duration, disk latency, CPU usage, and memory pressure under a consistent workload.

Build a Repeatable Troubleshooting Process

A reliable investigation begins with a baseline.

Record how long the build takes and identify the slowest stages using detailed Docker output.

Next, monitor disk activity using iostat, check process-level I/O with pidstat, and examine memory pressure using vmstat.

Review Dockerfile layer ordering, cache reuse, and build context size.

Make one change at a time, then repeat the build under comparable conditions.

Avoid clearing caches or changing multiple system settings during the initial investigation, because doing so makes the results harder to interpret.

The objective is not simply to achieve a faster build once. It is to understand why it was slow and develop a reliable solution.

Conclusion

Slow Docker builds are not always caused by Docker itself. Storage latency, memory pressure, unnecessary filesystem operations, and ineffective caching can all contribute to longer build times.

Start by identifying the slowest build stage, then use Linux monitoring tools to investigate the relevant system resources.

Optimise Dockerfile caching before considering hardware upgrades.

The most effective performance improvement is the one supported by actual measurements.