Fifty-five seconds. That's the whole story: in June 2026, Render reported that the median Docker build on its platform had fallen from 87 seconds to 32 — a 60% cut achieved not with faster chips, but with upload tuning, scheduling fixes, and cache plumbing. Then in August, it came back for the rest, moving builds onto faster CPU-and-disk nodes for another 40% off median build times across every runtime.
If you run a platform — or you're deciding whether to keep paying for one — those two changelog entries are worth more than most performance guides. They itemize, with production medians attached, exactly where time goes in a git-push container build. And every item on the list is reproducible on hardware you own.
The 55 seconds, itemized up front
Render's June 11, 2026 changelog names four optimizations behind the 87-to-32 drop:
| # | Lever | What it fixes |
|---|---|---|
| 1 | Tuned chunk size and parallelism for build image uploads | Layer-push throughput: many small layers saturate a connection better in parallel with right-sized chunks |
| 2 | Faster build scheduling on Render infrastructure | Queue time: how long a build waits for a builder before doing anything at all |
| 3 | Parallelized image upload with cache export | Pipeline tail: pushing the final image and writing the build cache used to run serially |
| 4 | Build cache stored in Render's own registry, with automatic pruning | Cold cache: every build reusing prior layers instead of rebuilding the world, without unbounded cache growth |
No new CPUs. No exotic builder fleet. The median build was spending most of its life waiting — waiting for a builder, waiting on uploads, waiting on a cache that wasn't there. That is the single most important sentence in this post: a typical PaaS build is a waiting problem, not a compute problem. Keep it in mind, because it dictates the entire self-hosted recipe.
Where the 55 seconds lived
Uploads: the quiet majority. A Docker build on a hosted platform moves bytes twice — the build context goes up, and the resulting layers come back down into a registry. For a typical web app image (a few hundred megabytes across a dozen-plus layers), layer-push round trips dominate wall-clock time once compilation itself is cached. Tuning chunk size and request parallelism is unglamorous work, but on high-latency paths between builder and registry it is the difference between a connection that idles and one that stays saturated. Render listed it first for a reason.
Scheduling: the invisible tax. Before a single Dockerfile instruction runs, a build waits for a free builder with capacity. On a shared multi-tenant fleet, that queue is where p50 goes to die: a few seconds of scheduling delay on every build adds up to a large slice of an 87-second median. Speeding up scheduling — placing builds faster, keeping warm capacity — buys wall-clock time with zero change to the build itself. This is pure platform engineering, and it's the lever most self-hosted setups accidentally leave on the table by provisioning builders on demand instead of keeping a warm pool.
The serial tail. Traditionally, a build exports the final image, pushes it, and then writes its layer cache — three steps in a trench coat walking single file. Parallelizing the image push with the cache export collapses the tail of every build. It changes nothing about what gets computed; it just stops doing two network-bound jobs one after another.
The cache, made durable. This is the big one. A build cache that evaporates between builds — because the builder was ephemeral, or the cache lived on a disk that got reclaimed — turns every deploy into a near-cold build: dependencies re-downloaded, compilation units rebuilt, base layers re-pulled. Storing the cache in a registry makes it survive builder churn, and automatic pruning keeps it from growing into a liability (stale cache entries slow down cache resolution and eat storage budget). Render's phrasing matters here: registry-backed plus pruned. A cache without eviction policy is a future incident.
Then August finished the job from the other direction: faster CPU and disk on the build nodes, worth another 40% median reduction across all runtimes. Note the ordering. Render squeezed the waiting out first and only then paid for faster hardware. That sequence is the correct one, and it tells you what to copy.
The self-hosted recipe, lever for lever
Every one of Render's four levers has a direct owned-hardware equivalent. Here they are in the same order.
1. Uploads: keep the builder close to the registry. On your own fleet, builder-to-registry traffic rides your own network — private networking between nodes rather than trips across the public internet. Run the registry in the same region (or the same cluster) as the builders, enable registry-backed layer caching, and you get Render's upload tuning essentially for free:
docker buildx build \
--cache-to type=registry,ref=registry.internal/app:buildcache,mode=max \
--cache-from type=registry,ref=registry.internal/app:buildcache \
--push -t registry.internal/app:latest .mode=max caches every stage, not just the final one — the difference between "rebuilds are fast when only the last layer changed" and "rebuilds are fast, period."
2. Scheduling: run warm, persistent builders. This is where self-hosted setups most often lose to managed platforms — not on compute, but by building on cold, ephemeral runners. The fix is a long-lived BuildKit daemon with a persistent cache volume, addressed remotely:
docker buildx create --driver remote --name fleet-builder \
tcp://builder-01.internal:1234A persistent buildkitd keeps its local layer cache hot between builds, so repeat deploys for the same service skip straight to the changed layers. Operators who have compared approaches converge on the same conclusion: a persistent BuildKit daemon with a cache volume beats spinning up a fresh builder per build for exactly the "watch a repo, rebuild often" loop a PaaS runs all day. (Note that Kaniko — the classic daemonless in-cluster builder — is now archived upstream, so BuildKit is also simply the maintained choice.)
3. The tail: BuildKit already pipelines export and push. Modern BuildKit parallelizes pushing the result image with exporting cache metadata when both target registries — you get Render's third lever by using a current builder and pointing --cache-to at the registry rather than a local directory that then needs a separate upload step.
4. The cache: registry-backed, with a pruning policy you actually enforce. Two mechanisms stack here. Registry cache (--cache-to/--cache-from above) survives builder replacement. BuildKit cache mounts handle the inside of a build step — dependency directories that should persist across builds without becoming image layers:
RUN --mount=type=cache,target=/root/.npm \
npm ci --prefer-offlineRUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/usr/local/cargo/git \
--mount=type=cache,target=/build/target \
cargo build --releaseReal-world reports put the payoff at an order of magnitude or more on the common case: one Rust project measured an incremental rebuild after a one-line change at 6 seconds against a full-tree rebuild before — roughly 16 to 50 times faster. The gotcha to respect: cache mounts are not image layers, so compiled artifacts must be copied out to the build context before the runtime stage can use them. And mirror Render's discipline on pruning — schedule docker buildx prune (or registry garbage collection) so stale cache doesn't accumulate silently.
The August lever: fast local disk on the builder. NVMe-backed builders are the cheapest hardware upgrade in this whole recipe, and it lands exactly where Render's follow-up landed: dependency unpacking, layer compression, and local cache I/O are all disk-bound long before they're CPU-bound.
What a budget builder box realistically delivers
So what does this recipe produce on modest owned hardware — say, a single-digit-euro-per-month cloud builder in the Hetzner CX class? Set expectations by build temperature, because temperature is the whole game:
- Cold build (nothing cached: fresh builder, new service): minutes, dominated by dependency downloads and full compilation. No platform on earth avoids this; Render's 87-second median was never measuring cold builds either — medians over a fleet with cache hits baked in are mostly warm.
- Warm rebuild (dependency change only): tens of seconds. Base layers and the build cache hit; only the dependency layer and above rebuild.
- Warm rebuild (app-code change only): seconds to tens of seconds. Everything below the final COPY hits cache; the platform pushes a few changed layers.
That warm-code-change number is your Render-32-seconds comparator, and it is entirely reachable: warm buildkitd plus registry cache plus cache mounts puts the typical Node or Python service rebuild in the same tens-of-seconds band, on hardware that costs less per month than a single seat of most hosted platforms.
And here is the part the TODO item insisted on, because it's the part everyone gets backwards: the biggest wins are cache locality and scheduling, not raw CPU. Doubling builder cores does almost nothing for a warm rebuild that spends 80% of its time pushing layers and resolving cache. A small builder with a hot cache beats a large builder with a cold one every time. Spend your budget in this order: persistent builders first (kill cold starts), registry-backed cache with pruning second (survive builder churn), builder-registry network proximity third (kill upload waits), and only then bigger CPUs or faster disks.
The honest limits, stated plainly:
- Someone has to run the pruning, rotate the registry credentials, and keep buildkitd healthy. Managed platforms fold that labor into the bill; self-hosting itemizes it as your time.
- Cache mounts and registry caches are accelerants, not magic: a monorepo with poor layer ordering (source copied before dependencies installed) defeats every cache on this page. Dockerfile hygiene — dependencies first, source last,
.dockerignorekept tight — is a prerequisite, not an optimization. - Multi-architecture builds (amd64 + arm64) roughly double build work unless you provision native builders per arch or accept emulation overhead. Depot's observation holds generally: native CPUs are a large share of remote-builder speed, so match builder arch to deploy arch.
Builds are a cache-locality problem
Render's two changelog entries tell a clean story when read together: first they eliminated the waiting (uploads, scheduling, serial tails, cold caches) for a 60% cut, then they bought faster hardware for another 40%. The sequence is the lesson. Teams that start by renting bigger builders are optimizing the smallest slice of the pie; teams that start with warm builders, registry-backed caches, and sane layer ordering get the hosted numbers on owned metal.
That ordering is also why build performance belongs in the self-hosting decision at all. Slow builds are routinely cited as a reason to stay on a managed platform — but the mechanism behind fast managed builds isn't proprietary. It's BuildKit, a warm cache, and a short network path to a registry: all open, all operable, all cheap to run.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.
Sources
- Render Changelog, "Reduced median Docker service build time by 60%," June 11, 2026 — 87s median (week of March 15) to 32s; four optimizations named (https://render.com/changelog/reduced-median-docker-image-build-time-by-60-percent)
- Render Changelog, August 7, 2026 — builds on faster CPU/disk nodes; 40% median reduction across all runtimes (https://render.com/changelog)
- Depot, "Introducing Depot — a faster way to build Docker images" — persistent builders (4 CPU, 8 GB RAM, 50 GB SSD cache), shared team cache, native CPUs (https://dev.to/depot/introducing-depot-a-faster-way-to-build-docker-images-bd3)
- BuildKit Dockerfile reference —
RUN --mount=type=cachesemantics (https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md) - Kaniko project status — archived upstream; BuildKit/Buildah as maintained alternatives for unprivileged builds



