Skip to content
Latchkey
Published June 2026 by Kaveh Alemi

The 2026 Docker Build Benchmark

How long container image builds really take in CI, and how layer caching, image size, and registry pulls move the number from a six-minute tax to a sub-two-minute step.

6.4 min
median cold Docker image build with no layer cache (modeled)
Latchkey analysis (modeled)
73%
of build time recoverable at a warm layer cache hit rate (modeled)
Latchkey analysis (modeled)
0.0025
managed runner $/min the build is billed at vs 0.008 Linux hosted
GitHub Actions pricing

Executive summary

Container builds are one of the heaviest single steps in a modern pipeline, and in 2026 most teams still pay full price for them on every push. A cold image build with no layer cache spends the bulk of its minutes re-running steps that did not change since the last commit, which is exactly the work a good cache layer makes free. The container build is, in this sense, a microcosm of the broader CI cost problem: a long, frequent, expensive step where most of the cost is mechanical waste rather than necessary work.

This benchmark quantifies where those minutes go. We model build wall-clock time, layer cache hit rate, final image size, and registry pull latency across common application shapes, then price the recovered minutes against published GitHub-hosted runner rates. The pattern is consistent across shapes: the teams that hold a high layer cache hit rate turn a multi-minute build into a sub-two-minute one, and a managed runner bills the remaining minutes at a fraction of the hosted Linux rate, so the saving stacks on top of the time saved.

The mechanism behind the savings is the Docker layer cache, which is conceptually simple and operationally finicky. A Dockerfile is a sequence of layers, each a function of its inputs, and a layer whose inputs have not changed can be restored instead of rebuilt. The catch is that the cache has to live somewhere durable. On an ephemeral CI runner the cache is born and dies with the job, so every build is effectively cold unless the cache is pushed to and pulled from a remote store. That single architectural decision, where the cache lives, separates the teams with sub-two-minute builds from the teams paying six minutes on every push.

Image size is the second lever, and it is the one teams notice last. A large final image is a tax paid on every downstream job: more to push at the end of the build, more to pull at the start of every job that consumes it, and more surface to scan for vulnerabilities. In a fan-out matrix where dozens of jobs pull the same image, a base-image choice that looks like a detail at build time becomes a recurring latency cost multiplied across the whole grid.

The throughline of this benchmark is that container build time is almost entirely addressable through configuration rather than code. A team does not need to rewrite its application or restructure its services to cut its build time by two-thirds. It needs a durable remote layer cache, a lean base image, and a runner layer that keeps the cache warm and bills the residual minutes cheaply. Those are pipeline decisions, and this report orders them by payoff.

Docker image build time by cache state
Cold (no cache)6.4 minPartial cache3.8 minWarm cache1.7 minFull cache (no change)0.6 min

Modeled wall-clock build time for a typical multi-stage application image. · Source: Latchkey analysis (modeled)

Layer cache hit rate by build setup
No remote cache11%Registry cache54%Inline cache67%Managed layer cache88%

Share of image layers served from cache across common CI configurations. · Source: Latchkey analysis (modeled)

Email me the report

The full report is right here on this page, free. Want the link in your inbox to read later or share, plus new Latchkey reports as they drop? Drop your email and we will send it over.

Sent! Check your inbox for the report link.

No spam. Unsubscribe anytime.

Most cold build minutes rebuild layers that did not change

On a cold build with no remote layer cache, nearly half the wall-clock time goes to re-executing steps whose inputs are identical to the previous commit. The dependency-install layer reinstalls the same packages, the build layer recompiles the same sources, and the result is bit-for-bit what the last run produced. That work is pure waste, and it recurs on every single push until a cache is introduced.

When we decompose a cold build minute, rebuilding unchanged layers is the largest slice, dependency fetch is the next, base image and registry pull follow, and push plus export takes the remainder. The unchanged-layer rebuild and the dependency fetch together dominate, and both are exactly the parts a layer cache eliminates. The base pull and the export are smaller and harder to remove, which is why caching, not network tuning, is the high-return lever.

The reason so many teams leave this on the table is architectural rather than a lack of awareness. Their cache lives only on the ephemeral runner that just got torn down, so it never survives to the next build. The cache exists, it is just born and killed inside a single job, which makes every build effectively a cold build. Moving the cache to a durable remote store is the change that turns the chart below from the common case into the exception.

  • Unchanged-layer rebuild and dependency fetch are the two largest slices of a cold build minute.
  • Both are eliminated by a layer cache; base pull and export are smaller and harder to remove.
  • The blocker is usually that the cache dies with the ephemeral runner, not that the team is unaware of caching.
Where a cold build minute goes
Rebuilding unchanged layers 47%
Dependency fetch 24%
Base image + registry pull 18%
Push + export 11%

Modeled split of a cold container build with no layer cache. · Source: Latchkey analysis (modeled)

A warm layer cache turns a six-minute build into under two

Once a remote layer cache holds the dependency and base stages, build wall-clock time drops sharply because only the application layers rebuild. In our modeling a cold build runs around six and a half minutes, a partial cache brings it under four, a warm cache lands near one and a half, and a build with no application changes finishes in well under a minute. The curve is steep precisely because the cached layers are the expensive ones.

The cache hit rate that produces those times depends heavily on setup. With no remote cache the hit rate is negligible, a registry-backed cache reaches roughly half, an inline cache does better, and a managed layer cache holds the highest rate because it persists across ephemeral jobs and is keyed carefully enough to stay valid without going stale. The difference between a fifty-percent and an eighty-eight-percent hit rate is the difference between a partial and a warm build on the time chart.

The savings compound across every push in a busy repository. A repository that builds its image fifty times a day is paying the cold-build penalty fifty times unless the cache is warm, and recovering several minutes per build at that volume is a large monthly number. This is why the layer cache hit rate deserves to be measured and monitored the way test coverage is, rather than configured once and forgotten until it silently stops hitting.

  • Build time drops from ~6.4 minutes cold to ~1.7 warm because the cached layers are the expensive ones.
  • Hit rate climbs from negligible with no remote cache to ~88% with a managed layer cache.
  • At high push volume the recovered minutes per build compound into a large monthly saving.

Cache configuration is where the hit rate is won or lost

The gap between a registry cache and a managed layer cache is not a small tuning detail, it is the difference between roughly half of layers served from cache and nearly nine in ten. The chart below shows the hit rate climbing across configurations, and the climb is driven by two things: where the cache lives and how its keys are scoped.

A cache keyed too loosely serves stale layers, which is dangerous because the build succeeds while producing the wrong image. A cache keyed too tightly almost never hits, which is merely wasteful. The configurations that win scope their keys to the lockfile and the base image digest so that a layer is reused exactly when its inputs are unchanged and rebuilt the moment they change. Getting that scoping right is most of the work, and it is the part teams most often get wrong.

A managed layer cache pushes the hit rate to the top of the chart by holding the cache in durable remote storage that every ephemeral job can read, while keeping the key discipline that prevents staleness. The result is that the cache behaves the way teams assume Docker caching already behaves, persistent and correct, rather than the ephemeral and effectively-cold reality of an unconfigured runner.

Image size is a registry-pull tax you pay on every job

A smaller final image is not just tidy, it is less to push at the end of the build, less to pull at the start of every downstream job, and less surface to scan on every security pass. The same application on a full OS base, a slim base, an Alpine base, and a distroless base spans an order of magnitude in compressed size, and that span shows up directly as pull latency wherever the image is consumed.

The cost is easy to underestimate because it is paid downstream rather than at build time. A fan-out matrix that runs the same image across twenty parallel jobs pays the pull cost twenty times, so a base-image choice that adds a few hundred megabytes adds that pull on every one of those jobs. Distroless and Alpine bases cut compressed size dramatically versus a full OS base, which is why the lean-base decision pays back across the whole grid rather than just once.

The trade-off is real and worth naming. Distroless images lack a shell and a package manager, which complicates debugging and some runtime tooling, and Alpine's musl libc occasionally surprises code that assumed glibc. The right call is usually to build and test on a fuller base and ship the lean one, capturing the pull savings in production and in fan-out CI without giving up developer ergonomics during the build itself.

Final image size by base choice
Full OS base (MB)940Slim base (MB)410Alpine base (MB)180Distroless (MB)96

Modeled compressed image size for the same app on different base images. · Source: Docker

Hosted build minutes are billed at the full Linux rate

A container build on GitHub-hosted Linux runners is billed at the published per-minute Linux rate for the entire wall-clock build, cache miss or not. There is no discount for a build that is mostly waste; a six-minute cold build costs six minutes regardless of how much of it was rebuilding unchanged layers. The meter does not know the difference between useful work and recomputation.

Because builds are long and frequent, they are one of the line items where the per-minute rate moves the monthly bill the most. A short job billed at a given rate is a small number; a multi-minute build billed at the same rate, run dozens of times a day across many repositories, is a large one. The container build is therefore a high-leverage place to attack both the minute count, through caching, and the per-minute price, through the runner choice.

This is the part teams tend to discover only when they decompose the bill. The container build does not announce itself as expensive; it is one step among many in a YAML file. But when the minutes are tallied and priced, the build step is frequently among the largest single contributors, which makes it a natural first target for both caching and a lower per-minute rate.

Managed runners cut the remaining minutes and keep the cache warm

The combination that wins is a persistent remote layer cache plus a lower per-minute rate, and those two levers reinforce each other. A managed runner holds the cache across ephemeral jobs so the hit rate stays high, which cuts the minute count, and bills the residual build minutes at a managed rate well below the hosted Linux price, which cuts the cost of each remaining minute. The savings multiply rather than add.

Consider the arithmetic on a representative build. Caching takes the build from cold to warm, removing the majority of the minutes. The managed per-minute rate then prices the surviving minutes at roughly a third of the hosted Linux rate. A team that only does one of the two captures part of the win; a team that does both captures the product, which is why the managed-plus-cache configuration sits at the favorable end of every chart in this report.

Crucially, none of this requires rewriting the Dockerfile or restructuring the pipeline. The application is unchanged, the build definition is largely unchanged, and the win comes from where the cache lives and what the minutes are billed at. That is what makes container-build optimization one of the highest-return, lowest-risk changes available to a team that ships images on every push.

  • A managed layer cache keeps the hit rate high across ephemeral jobs, cutting the minute count.
  • A managed per-minute rate near 0.0025 prices the residual minutes well below the 0.008 hosted Linux rate.
  • The two levers multiply, and neither requires changing the Dockerfile or the application.

Build optimization is configuration, not code, which makes it low-risk

Every lever in this benchmark is a configuration decision rather than an application change. Where the layer cache lives, how its keys are scoped, which base image the final stage uses, and what the runner minutes are billed at are all pipeline settings. None of them touch the code under test, which means none of them carry the regression risk that an application change would.

This is what separates container-build optimization from harder performance work. Speeding up the application itself is a real engineering project with real risk; speeding up the build of the application is mostly a matter of caching what does not change and shipping a leaner artifact. The payoff is large, the wall-clock build dropping by two-thirds and the remaining minutes costing a fraction, while the blast radius stays inside the pipeline configuration.

For a team deciding where to spend a limited optimization budget, that risk profile is decisive. The container build offers a large, measurable improvement that can be validated quickly and rolled back trivially, with no chance of introducing a behavioral bug in the product. It is, in the language of the broader CI cost discussion, mechanical waste, and mechanical waste is the cheapest kind to remove.

Recommendations

Move the layer cache off the ephemeral runner into durable storage

The single highest-return change is giving the layer cache somewhere to live that survives the job. A cache that dies with the runner makes every build effectively cold. A durable remote cache, ideally a managed layer cache that persists across ephemeral jobs, is what turns a six-minute cold build into a sub-two-minute warm one.

Scope cache keys to lockfiles and the base digest, then measure hit rate

A cache keyed too loosely ships stale layers; one keyed too tightly never hits. Scope keys to the lockfile and base-image digest so a layer is reused exactly when its inputs are unchanged, and monitor the hit rate the way you monitor test coverage so it does not silently degrade.

Build and test on a full base, ship a distroless or Alpine one

Final image size is a pull tax paid on every downstream job and multiplied across fan-out matrices. Keep developer ergonomics by building on a fuller base, then ship a lean distroless or Alpine image to capture the pull savings in CI and production without losing the shell and tooling at build time.

Target the build step when decomposing the Actions bill

Because builds are long and frequent and billed at the full per-minute rate regardless of cache state, the container build is often among the largest single line items. Tally and price the build minutes specifically; it is usually a high-leverage place to attack both the minute count and the per-minute price.

Pair the warm cache with a lower per-minute managed rate

Caching and a managed per-minute rate multiply rather than add. A managed runner keeps the cache warm to cut the minute count and bills the residual minutes near 0.0025 against the 0.008 hosted Linux rate, so doing both captures the product of the two savings rather than just one.

Outlook

Container builds will get more central, not less, as more of the delivery surface moves into images. The spread of containerized test environments, build-once-deploy-everywhere patterns, and image-based serverless packaging all push more pipeline minutes through the build step, which raises the payoff of getting it fast and cheap. A build-time inefficiency that was tolerable when images were a small part of the pipeline becomes a structural cost as they become the center of it.

The architectural direction is toward caches that are durable and managed by default rather than hand-wired per repository. The pattern of pushing a remote cache, scoping keys carefully, and pulling on the next build is well understood but fiddly to maintain across dozens of repositories, and teams increasingly want that handled by the runner layer rather than re-implemented in every Dockerfile. As that becomes the baseline, the cold-build penalty shifts from the common case to a sign of a misconfigured pipeline.

For most teams the practical takeaway is that the container build is among the best-return optimizations available, because the payoff is large and the risk is near zero. Cache what does not change, ship a leaner image, and bill the residual minutes cheaply, and a build that was a six-minute tax on every push becomes a sub-two-minute step nobody thinks about. The teams that do this spend the saved minutes shipping; the teams that do not keep paying for the same layers, day after day.

Methodology

This benchmark models container image build behavior across common application shapes (multi-stage builds, slim and distroless bases, registry and inline cache configurations) and prices the resulting minutes against published GitHub Actions runner rates. Build times, layer cache hit rates, image sizes, and the cold-build minute split are Latchkey modeled estimates labeled as such and intended to show direction and magnitude rather than a precise population value; per-minute pricing ties to the published GitHub-hosted Linux rate and the Latchkey managed rate. Figures labeled "modeled" are illustrative estimates derived from public pricing and typical pipeline shapes, not a primary survey; figures attributed to a named source reflect that source. Pricing reflects published rates at time of writing and should be verified against current provider pricing.

Sources

More Latchkey reports

See what you would save with Latchkey managed runners and self-healing. Start free → 30-day trial · No credit card