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. I wasn't looking in the right place until I measured it.
Workspaces need to start faster. Every minute between "create" and "ready" is a minute someone can't work.
I haven't measured the full startup path yet, so I don't know where all that time goes. The last measurement found wasted work somewhere I hadn't expected. I'm starting this pass 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. Docker starts by collecting the "build context." It walks the folder you gave it and sends everything there to the build engine before one Dockerfile instruction runs. Point it at a 9GB folder and every build starts by hauling 9GB, even when the image doesn't need those files.
The local context held about 8.9GB. As more development moved into automated agent workflows, output accumulated faster than anyone revisited the assumptions behind the root build context. Every build carried that output along.
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 found this without running a real build. The audit used metadata only: directory size
summaries, Dockerfiles, build scripts, the .dockerignore diff, and later
dry-run probes that mount a build context and measure what would transfer. I pushed no
images and changed no running environment.
The dry-run probes made the pattern hard to miss. The broad root context moved much more data than builds rooted in smaller directories. Same repository, different starting point. Each build paid for everything it could see before its first 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.
Ordering fixes that waste. Slow, stable steps go first. Fast-changing files go last, so a small edit doesn't throw away the expensive cached work.
The image split is follow-up work.
The audit also found room to split common tooling from profile-specific tooling. That could shrink cold pulls without taking needed tools away. This is follow-up work, and it isn't 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.
The build audit taught me what suspects are worth. The root context wasn't at the top of my list. If I had followed instinct, I might have tuned install steps while every build kept moving files it never used.
Next, 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. The build audit is done. The startup work isn't.
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.