March 16, 2022·6 min read·engineeringdockervirtiofsfilesystems

Docker Desktop 4.6 brings VirtioFS to Mac file sharing

Experimental VirtioFS changes how a Linux guest accesses Mac source files. The release addresses the cost of shared directories while exposing the importance of filesystem semantics across the virtual machine boundary.

A source directory shared with a Linux container on a Mac crosses an operating system boundary. The editor works with the host filesystem, while the container accesses files through a Linux guest. A workload that repeatedly opens small files can spend substantial time on that connection. Docker Desktop 4.6 introduces experimental VirtioFS sharing to improve this part of local development.

The 4.6.0 release notes date the release to March 14. Docker's March 16 explanation describes VirtioFS as an optional replacement for the default gRPC-FUSE implementation. It also describes changes to file synchronization. The performance claims concern those changes together, which matters when interpreting the results. They do not establish that replacing a protocol alone produces the same improvement for every workload.

I value Docker for making build inputs and output images explicit. A live source mount introduces a different requirement. Development needs the running container to see edits made outside its filesystem, and that ongoing interaction has costs that an image definition cannot remove.

A filesystem designed for the host and guest relationship

The Linux virtiofs documentation in the 5.16 source tree describes a guest mounting a directory exported by its host. The device provides filesystem access without requiring a network filesystem. Its design takes advantage of the fact that the host and guest run on the same machine.

The Linux guest uses the FUSE protocol for filesystem requests. In a conventional FUSE arrangement, a userspace process receives requests through /dev/fuse. VirtioFS replaces that connection with the virtio-fs device interface. The guest places requests into a virtqueue, the host fills the response buffers, and the guest processes completion. FUSE therefore remains part of the design. The change concerns the transport and the relationship between the guest filesystem client and the host server.

That distinction makes the mechanism easier to understand than a broad statement about faster volumes. A file access still has to reach the system that owns the directory. VirtioFS gives that exchange a device designed for guest and host sharing. It does not turn the Mac directory into an independent Linux filesystem or eliminate the need to agree on file behavior.

The kernel documentation also describes a priority problem introduced by the queues. A reader of /dev/fuse can choose which pending request to transfer. A virtqueue fixes the order of requests already enqueued. When the queue fills, high priority requests need another route. VirtioFS provides a separate hiprio virtqueue for those requests. Even this low-level detail reflects the larger goal of preserving filesystem behavior while changing how requests travel.

The same documentation notes that access-time behavior follows the underlying exported filesystem. Guest options such as noatime and strictatime do not independently control it. This is a useful example of a host property that remains relevant after the guest mounts a directory. Sharing requires more than passing bytes between processes.

Why small files make the boundary expensive

Docker's 2019 explanation of its Windows FUSE sharing implementation provides useful historical context. That implementation is a different backend on a different host operating system. Its explanation still makes the general source of overhead concrete. A filesystem request can cross kernel and userspace boundaries as well as the boundary between the guest and host.

The article describes a Linux VFS request reaching a FUSE client, travelling over hypervisor sockets, and reaching a host process that performs the operation through host filesystem APIs. This should not be read as the precise implementation of the new Mac VirtioFS path. It explains why host sharing has historically required more work than accessing files already local to the guest.

The difference becomes significant when a tool performs many filesystem operations. A dependency tree can contain many small files, so total data size gives an incomplete account of the workload. Opening and inspecting files can require repeated metadata work even when the contents are small. Improving that request path can benefit development without changing the application's own computation.

Docker's March explanation identifies dependency-heavy projects as a target. The architectural implication is that a local development bottleneck may come from how source reaches the guest. Increasing the compute resources assigned to the container does not directly remove the host access path. The new sharing option addresses a different part of the environment.

My interest is in reducing the workarounds that this distinction can impose on a project. A build definition should describe the application. Additional arrangements whose only purpose is to compensate for workstation file sharing make that definition harder to understand. Better integration can reduce the need for those arrangements without changing how the application ships.

Caching is part of filesystem correctness

The Windows FUSE article explains why caching appears in this history. Docker cached file attributes and directory contents to avoid repeated requests. File contents could also remain in the kernel cache. Each avoided request reduced the number of transitions needed to satisfy an operation.

Those optimizations required invalidation when the host changed files. Docker used host filesystem events both to invalidate cached entries and to deliver change notifications into the Linux environment. The article describes the extra work needed to make a host-side deletion produce a Linux notification. Development tools that watch source files depend on that correspondence between operations and events.

This history helps explain why a faster shared filesystem must preserve behavior across more than a single read. A container can inspect a file, another process can change it, and a watcher can need notification of that change. Caching may make the initial inspection cheaper while also creating state that must remain consistent with subsequent activity. Benchmarks focused only on reading existing data leave those interactions outside their results.

The 4.6 release notes identify a concrete VirtioFS issue involving different Unix user IDs. A process running as root can query a file, after which an immediate access by a process running as nginx can return Permission Denied. The notes describe caching issues in this situation. That is an observable correctness problem for applications that access shared files through more than one identity. A directory being reachable does not establish that every process will observe the expected permissions.

Enabling the experimental implementation

Docker's March 16 announcement gives explicit host requirements. Apple Silicon users need macOS 12.2 or later. Intel users need macOS 12.3 or later. In Desktop 4.6, the Preferences panel's Experimental Features section contains both the new Virtualization framework option and the VirtioFS accelerated directory sharing option. Docker instructs users to enable both and select Apply & Restart.

These requirements connect the filesystem choice to the virtual machine configuration. Installing the Desktop release alone does not select the experimental sharing implementation. An account of its behavior therefore needs to include the host version and the enabled settings. Otherwise two installations described as Desktop 4.6 can still exercise different filesystem paths.

The announcement makes the option available across the Personal and paid subscription tiers. It remains experimental. Docker acknowledges permission errors and points to an experimental build on its public discussion thread, with stability still to be confirmed. That statement does not establish a correction in the released 4.6 package. The release's known issue remains relevant to anyone evaluating the shipped version.

What changes for development

VirtioFS addresses the interaction between host source files and the Linux guest. It does not make every slow build a filesystem problem. A command that mostly performs computation has a different cost structure from one that repeatedly opens files in a shared directory. The useful architectural question is which operations actually cross that shared directory boundary during the command.

For an engineer reading a Dockerfile, this release adds context to local behavior that the file alone cannot explain. An application image can be unchanged while its development mount uses a different sharing implementation. The same source tree may then produce a different waiting time or expose a different permission interaction. Keeping those facts separate makes a performance report more precise.

I regard the experiment as relevant because it targets an everyday dependency of container development on Mac. Its most consequential property is the attempt to improve host sharing while retaining the filesystem behavior applications expect. The documented interaction between caching and multiple user IDs shows exactly where that requirement still needs attention in the released implementation.