August 21, 2020·4 min read·engineeringdockerbuildkitfilesystems

Docker Desktop Edge changes its build and file-sharing defaults

BuildKit and gRPC-FUSE address different costs in local development. The August Edge release makes them defaults under specific conditions and removes the Mutagen experiment.

Local container development can spend time building an image and time accessing files shared with a running container. Those operations have different implementations, even when both appear as delays after an edit. Docker Desktop Edge 2.3.5.0 changes defaults in both areas. Its 21 August release notes enable BuildKit after a factory reset and make gRPC-FUSE the default Mac file-sharing implementation.

The factory-reset condition matters. The announcement does not say every existing builder configuration changes immediately after an upgrade. Docker also documents separate ways to restore the earlier choices. BuildKit can be disabled in the Docker Engine preferences, while gRPC-FUSE can be disabled in General preferences to return to osxfs. These are independent settings for independent parts of the development environment.

I care about build defaults because the Dockerfile is the repeatable account of how an image is produced. A different builder can change the work required to produce that image without requiring a different application description. File-sharing defaults deserve the same precision because a development container can depend on host files that are outside the image itself.

BuildKit represents build dependencies explicitly

The BuildKit 0.7.2 project documentation describes LLB, its binary intermediate format for build dependency graphs. A Dockerfile frontend produces that representation. The graph supports concurrent execution and caching, while the project separates its frontend from the worker that performs build operations. BuildKit is consequently more than a different presentation of the legacy builder's progress output.

An explicit dependency graph gives the builder information about which work depends on which inputs. Operations that have no dependency relationship can be candidates for concurrent execution. The dependency relationship still constrains the result. Parallel execution cannot make an operation consume an output that has not yet been produced. That distinction explains the mechanism more usefully than describing BuildKit as universally faster.

The same documentation identifies concurrent dependency resolution and build-cache import and export among its capabilities. Those mechanisms affect different parts of a build. Resolving dependencies can overlap work, while cache reuse can avoid work whose result is available. They should not be collapsed into a single claim that every instruction executes more quickly.

Multi-stage builds expose useful structure

Docker's multi-stage build guide at this time describes naming stages and copying artifacts between them with COPY --from. It also permits a later stage to use an earlier stage as its base. Those relationships let a build file express common preparation and separate outputs without putting every tool into the final image.

The guide gives an example with a common builder stage followed by separate builds of two source files. The common stage installs the build tools. Each later stage adds a different source and produces its own binary. That structure describes actual dependencies more accurately than placing unrelated compilation steps into one undifferentiated instruction sequence.

My preference for a multi-stage build ending with a static binary in scratch follows the same concern about separating build requirements from runtime contents. The builder needs tools that the final image does not. Keeping that separation in the Dockerfile makes the intended artifact easier to inspect, irrespective of how the builder schedules the stages.

The existing Docker command remains the entry point

The contemporary BuildKit integration guide says the existing Dockerfile format works with BuildKit and its images can be pushed to Docker Hub. It describes the integration for Linux-container builds and gives an environment variable for selecting it explicitly. This shell-neutral form makes the same selection for one command.

env DOCKER_BUILDKIT=1 docker build .

That command is useful when explaining an environment that has not adopted the new default. It states the builder choice alongside the build request. The integration guide also documents plain progress output through the progress option, which gives another way to inspect what the builder is doing without changing the Dockerfile.

The August change moves a previously explicit selection into the reset configuration. That can make the improved implementation available without requiring every developer to configure the environment variable. It also increases the importance of stating the reset condition when comparing two installations that may retain different settings.

The integration guide also distinguishes the builder from the Dockerfile frontend used for additional syntax. It documents a syntax directive that selects a frontend image, with a network connection needed to retrieve custom frontends. Enabling BuildKit is therefore one choice, while selecting an extended Dockerfile syntax is another. An existing Dockerfile can benefit from the builder integration without immediately adopting every experimental instruction supported by a separately selected frontend. This keeps the default change understandable as a change in execution, with further syntax choices remaining visible in the build description.

File sharing changes a separate path

The Mac release notes describe gRPC-FUSE as reducing file-sharing cost and CPU use compared with osxfs, particularly under many host file events. They provide no universal multiplier for an arbitrary application. The useful claim is the change of implementation and the workload characteristic Docker calls out. A running container accessing shared source files is exercising that path even when no image build occurs.

Docker also removes the Mutagen file-synchronization experiment in this release and says it is reconsidering the integration based on feedback. Earlier Edge notes described automatic two-way synchronization for delegated mounts. Removing that experiment is distinct from selecting gRPC-FUSE. It would be inaccurate to describe the release as simply making the synchronization experiment the default.

This matters when reasoning about an edit followed by a running application's response. Image construction and shared-file access can both be involved in a development workflow, but only one may be exercised by a particular action. A rebuild uses the builder. A process reading a bind-mounted source file uses the sharing implementation. Edge 2.3.5.0 changes both defaults, while the separate controls preserve a way to identify which implementation is relevant to the operation being observed.