Vol. XV / Issue 13

The McKinnie Dispatch

Filed from the build context

Performance Managed workspaces 2026

Make the list before you fix anything

Before I tried to fix workspace startup, I measured the builds.

One .dockerignore pass cut a local Docker build context from about 8.9GB to about 2.4GB. That was enough to remind me not to guess where workspace startup time goes.

The request keeps coming up: make workspaces start faster. It's a fair ask. The wait between "create" and "ready" is time someone could spend working.

I don't know where all of that startup time goes yet because I haven't measured the full path. I do know what happened the last time I started with the measurement. The biggest source of wasted work was not where I expected it. That is why this pass starts with a stopwatch instead of a hunch.

The build context is a tax on every image.

In early June the ask was Docker build speed. When you run a Docker build, the first thing Docker does is collect the "build context." It walks the folder you point the build at and ships everything in that folder to the build engine. This happens before a single instruction in your Dockerfile runs. If the folder holds 9GB, every build starts by hauling 9GB, whether the image needs those files or not.

The local context held about 8.9GB. As more development work moved into automated agent workflows, the repository accumulated output faster than the assumptions around the root build context were revisited. The build kept carrying all of it.

The fix was one pass on the root .dockerignore, the file that tells Docker what to leave out. That cut the context from about 8.9GB to about 2.4GB without changing the image. The build was doing several gigabytes of local work that added nothing to the result.

You can find this without running a build.

I didn't run a single real build to find any of this. The audit was metadata only: directory size summaries, reading the Dockerfiles and build scripts, diffing the .dockerignore, and later some dry-run probes that mount a build context and measure what would actually transfer. No image pushes, no changes to any running environment.

The dry-run probes also made the pattern clear. The broad root context still moved much more data than builds rooted in smaller directories. Same repository, different starting point. The difference was what each build could see before the first build step ran.

A build rooted at a small directory pays a small tax. A build rooted at the repo root pays for everything.

The cache was doing exactly what we told it.

The second finding was about layer caching. Docker caches each build step. When a step's inputs change, that step rebuilds, and so does every step after it, cached or not. Order matters a lot.

Some workspace image builds copied frequently changed runtime files before slower install steps. A small local edit could invalidate the expensive part of the cache and repeat work that had not changed.

The fix is ordering. Put the slow, stable steps first and the fast-changing files last, and the cache does its job.

The image split is follow-up work.

The audit also found room to separate common tooling from profile-specific tooling. That could make cold pulls smaller without taking useful tools away from the people who need them. It is follow-up work. It is not done yet.

So where does startup time actually go?

I have suspects. A cold image pull is an obvious one. So is the time spent preparing storage and running the workspace's first-boot tasks.

But suspects are what the build audit warned me about. Before that pass, the root context was not at the top of my list. If I had optimized on instinct, I could have tuned install steps while every build kept moving files it never used.

The next step is specific: measure one representative workspace launch from start to ready. Separate the image pull, storage preparation, first boot, and ready signal. Then fix the biggest number first. The build audit is done. The startup work is not.

The first bottleneck I found was not at the top of my list. That's enough reason to make the list before I fix anything.