Docker Desktop's Performance Odyssey Over a Year of Innovations
By Vladimir Mikhalev · Docker Captain · CNCF Ambassador · IBM Champion
Docker Desktop changed a lot during 2023, and most of it landed where you feel it day to day: how fast the daemon comes up, how fast a bind mount moves bytes, how fast a container reaches the internet, and how much memory the whole thing holds when it is doing nothing. This post is a reading of what Docker published about those releases, with each number attributed to where it came from, plus what I saw on my own Docker Desktop v4.22.0 install while following along.
NOTESources
The measurements in this post are not mine unless the caption says so. They come from two places:
- Docker’s own engineering write-up, Unleash Docker Desktop 4.22: The Featherweight Heavy-Hitter for Supercharged Rapid Development (David Scott, August 2023), with the release posts for 4.19 and 4.21.
- Ajeet Singh Raina’s hands-on runs on Collabnix: Unlocking High Performance with VirtioFS in Docker Desktop (June 2023) and Docker Desktop for Mac is no longer slow, and how the Docker team fixed it (January 2024). Earlier versions of this post reproduced benchmark output shared by Ajeet Singh Raina without attribution; corrected in October 2026.
Three screenshots are my own, taken on Docker Desktop v4.22.0 on my Mac, and are labelled as such.
2023: what Docker shipped, release by release
1. Daemon startup time
Docker’s 4.21 release post states that launching Docker Desktop on Apple Silicon is at least 25% quicker than previous versions and that, after a fix to a startup bug, Docker starts in four seconds on Apple Silicon. Source: Docker Desktop 4.21 release post, July 2023. I have no per-platform table of my own to add; the earlier version of this post carried one without a source, and it is gone.
2. Container operations with hyperfine
The runs below are Ajeet Singh Raina’s, comparing Docker Desktop v4.22 against v4.11 with hyperfine. Source: Collabnix, “Docker Desktop for Mac is no longer slow”. Output reproduced as published.
To install hyperfine:
brew install hyperfineThe benchmark command:
hyperfine -i -p 'docker stop ubuntu || :' 'docker run -it --rm ubuntu echo'What the flags do:
-itells hyperfine to ignore a non-zero exit code from the benchmarked command.-psets a preparation command that runs before each timing run.- The preparation command,
docker stop ubuntu || :, stops a container named ubuntu if one is running and otherwise does nothing. - The benchmark command,
docker run -it --rm ubuntu echo, starts an ubuntu container, runsecho, and removes the container.
Results for Docker Desktop v4.22
First Attempt:
hyperfine -i -p 'docker stop ubuntu || :' 'docker run -it --rm ubuntu echo'Benchmark 1: docker run -it --rm ubuntu echoTime (mean ± σ): 44.3 ms ± 3.3 ms [User: 29.5 ms, System: 12.7 ms]Range (min … max): 39.7 ms … 49.6 ms 10 runsWarning: Ignoring non-zero exit code.Second Attempt:
hyperfine -i -p 'docker stop ubuntu || :' 'docker run -it --rm ubuntu echo'Benchmark 1: docker run -it --rm ubuntu echoTime (mean ± σ): 46.3 ms ± 2.3 ms [User: 29.4 ms, System: 12.4 ms]Range (min … max): 43.1 ms … 52.5 ms 28 runsWarning: Ignoring non-zero exit code.Third Attempt:
hyperfine -i -p 'docker stop ubuntu || :' 'docker run -it --rm ubuntu echo'Benchmark 1: docker run -it --rm ubuntu echoTime (mean ± σ): 46.5 ms ± 5.3 ms [User: 29.5 ms, System: 12.1 ms]Range (min … max): 39.0 ms … 63.5 ms 28 runsWarning: Ignoring non-zero exit code.The first attempt averaged 44.3 milliseconds, give or take 3.3 milliseconds. So most runs landed between 41.0 and 47.6 milliseconds. Fastest was 39.7, slowest 49.6.
Second attempt: 46.3 milliseconds on average, deviation 2.3 milliseconds. Most runs sat between 44.0 and 48.6 milliseconds. The quickest hit 43.1, the longest 52.5.
Third attempt was noisier. Average 46.5 milliseconds, but the deviation jumped to 5.3 milliseconds. That spreads the bulk of runs across 41.2 to 51.8 milliseconds. They ranged from 39.0 all the way up to 63.5.
Results for Docker Desktop v4.11
First Attempt:
hyperfine -i -p 'docker stop ubuntu || :' 'docker run -it --rm ubuntu echo'Benchmark 1: docker run -it --rm ubuntu echoTime (mean ± σ): 59.7 ms ± 3.2 ms [User: 36.4 ms, System: 15.6 ms]Range (min … max): 55.0 ms … 68.3 ms 13 runsWarning: Ignoring non-zero exit code.Second Attempt:
hyperfine -i -p 'docker stop ubuntu || :' 'docker run -it --rm ubuntu echo'Benchmark 1: docker run -it --rm ubuntu echoTime (mean ± σ): 56.7 ms ± 2.7 ms [User: 36.3 ms, System: 15.3 ms]Range (min … max): 51.7 ms … 62.5 ms 13 runsWarning: Ignoring non-zero exit code.Third Attempt:
hyperfine -i -p 'docker stop ubuntu || :' 'docker run -it --rm ubuntu echo'Benchmark 1: docker run -it --rm ubuntu echoTime (mean ± σ): 59.8 ms ± 4.5 ms [User: 36.4 ms, System: 15.3 ms]Range (min … max): 51.2 ms … 69.5 ms 21 runsWarning: Ignoring non-zero exit code.Older version, slower numbers. First attempt averaged 59.7 milliseconds with a 3.2 millisecond deviation, so most runs fell between 56.5 and 62.9 milliseconds. Quickest was 55.0, longest 68.3.
Second attempt came in a bit better: 56.7 milliseconds on average, deviation 2.7 milliseconds. Most runs landed between 54.0 and 59.4 milliseconds. The spread ran from 51.7 to 62.5.
Third attempt averaged 59.8 milliseconds, deviation 4.5 milliseconds. Most runs were between 55.3 and 64.3 milliseconds. Fastest 51.2, slowest 69.5.
Final Results (Docker Desktop 4.11 vs 4.22)
| Docker Desktop v4.11 | Docker Desktop v4.22 | |
|---|---|---|
| First Attempt | 55.0 - 68.3 ms | 39.7 - 49.6 ms |
| Second Attempt | 51.7 - 62.5 ms | 43.1 - 52.5 ms |
| Third Attempt | 51.2 - 69.5 ms | 39.0 - 63.5 ms |
Across the three attempts, 4.22 starts and tears down a trivial container roughly 10 to 15 milliseconds faster than 4.11 on the same machine. Those are Ajeet’s numbers; mine are in the rerun section at the end.
3. Resource efficiency
Resource Saver arrived in 4.21 for CPU and in 4.22 for memory. It watches for the engine sitting idle with no containers for 30 seconds and then drops the VM’s footprint. Docker’s post puts the idle memory reduction at 10x. Source: Unleash Docker Desktop 4.22.
You can watch it happen: start Docker Desktop, run nothing for 30 seconds, and a leaf icon appears in the whale menu and the dashboard footer.

My own run on Docker Desktop v4.22.0.
Docker’s chart of total idle memory across 4.20, 4.21 and 4.22:

Source: Docker, Unleash Docker Desktop 4.22, David Scott, August 2023. Reproduced from the post.
4. File sharing with VirtioFS
File sharing between the Mac and the Linux VM has been the sore spot of Docker Desktop for years. VirtioFS became the default on macOS 12.5 and later in 4.21, and Docker’s own figure for the effect is that a clean build of redis/redis checked out on the host went from seven minutes on 4.20 to about two minutes on 4.21. Source: Docker Desktop 4.21 release post.
The file-sharing mechanisms Docker Desktop has used
Types of File Sharing Mechanism
| File Sharing Mechanism | Description | Additional Notes |
|---|---|---|
| VirtioFS | A native file sharing mechanism supported by Docker Desktop. It’s the fastest as it doesn’t need extra software. | Default file-sharing mechanism in DD 4.22. |
| gRPC FUSE | Uses the gRPC protocol for file sharing. While slower than VirtioFS, it’s faster than other mechanisms. | Default option in DD 2.4.0.0 (2020). Consumes less CPU than osxfs, especially with numerous file events on the host. |
| qemu-grpcfuse | Uses the qemu hypervisor for file sharing. It’s the slowest mechanism but viable for building Redis images. | |
| hyperkit-grpcfuse | Employs the hyperkit hypervisor for file sharing. Its speed is comparable to qemu-grpcfuse. | |
| osxfs | A file system driver bridging macOS file system and the Linux-based system used by Docker containers. | Default file-sharing mechanism in Docker for Mac 1.12.x. |
Enable VirtioFS
Open Docker Desktop settings, go to General, find “Choose file sharing implementation for your containers” and pick VirtioFS, then Apply & restart.

My own Docker Desktop v4.22.0.
A bind-mount throughput test
The test below, and the two tables that follow it, are Ajeet Singh Raina’s. Source: Collabnix, “Unlocking High Performance with VirtioFS in Docker Desktop” (June 2023) for the 1 GB copy, and Collabnix, “Docker Desktop for Mac is no longer slow” for the 10 GB tables. The output is reproduced as published; the home directory in it is his, not mine.
Steps to Set Up and Test VirtioFS
Create a directory using the command:
mkdir dataPopulate the Directory with a Large Data File (1GB of random data) using the command:
dd if=/dev/zero of=data/data.img bs=1M count=1000Next, let’s build the Docker Image using the command:
First, create a Dockerfile with the following content:
FROM ubuntu:latest
VOLUME /data
CMD ["bash"]Then, build the Docker image using the command:
docker build -t virtiofs-demo:latest .Run the Docker Container using the command:
docker run -it --rm -v "$(pwd)/data:/data" virtiofs-demoList the Running Containers using the command:
docker psExpected output:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESe47eb4731c80 virtiofs-demo "bash" 5 minutes ago Up 5 minutes gifted_lichtermanNext, measure File Copy Time into the Container
time docker cp gifted_lichterman:/data/data.img data/data.imgExpected result:
Successfully copied 1.05GB to /Users/ajeetsraina/july/virt/data/data.imgdocker cp gifted_lichterman:/data/data.img data/data.img 0.61s user 1.84s system 36% cpu 6.750 totalPerformance Comparison
Comparing Docker Desktop 4.22 vs 4.11 (without VirtioFS enabled)
| Data Size: 10GB Data Transfer | Docker Desktop v4.11 | Docker Desktop v4.22 |
|---|---|---|
| Run #1 | 7 min 13.21 s | 5 min 11.10 s |
| Run #2 | 6 min 47.89 s | 5 min 04.08 s |
| Run #3 | 7 min 04.75 s | 5 min 02.08 s |
Comparing Docker Desktop 4.22 vs 4.11 (with VirtioFS enabled)
| Data Size: 10GB Data Transfer | Docker Desktop v4.11 | Docker Desktop v4.22 |
|---|---|---|
| Run #1 | 2 min 18.55 s | 1 min 04.44 s |
| Run #2 | 2 min 20.23 s | 1 min 06.21 s |
| Run #3 | 2 min 15.65 s | 1 min 05.39 s |
Docker’s Redis build chart

Source: Docker, Unleash Docker Desktop 4.22, David Scott, August 2023. Reproduced from the post.
The earlier version of this post also carried a Redis build table across four file-sharing modes and a pgbench comparison. I could not find a public source for either, so both are gone.
5. Container networking
The big networking change of the year was 4.19 swapping VPNKit for the TCP/IP stack from the gVisor project on container-to-host traffic. Docker’s statement for that release is that container-to-host networking on macOS became 5x faster. Source: Docker Desktop 4.19 release post, May 2023. The August write-up adds an iperf3 figure on a first-generation M1 Mac mini: about 3.5 Gbit/s on the old stack against 19 Gbit/s on 4.19 and later. Source: Unleash Docker Desktop 4.22.
NOTEIn Docker Desktop 4.19, container-to-host networking performance was boosted by 5x on macOS, achieved by replacing vpnkit with the TCP/IP stack from the gVisor project.
If your containers reach out to external servers, npm install or apt-get pulling packages for instance, this is the speed-up you feel first.
To check which networking stack Docker Desktop is using:
cat ~/Library/Group\ Containers/group.com.docker/settings.json | grep -i networkIf you need VPNKit back, add "networkType":"vpnkit" to that file.
The earlier version of this post carried iperf3 scripts and per-platform throughput tables. They were not mine and I could not find their public source, so they are removed rather than attributed.
6. Apple Silicon and Rosetta
Docker Desktop can run amd64 images on Apple Silicon through Rosetta instead of QEMU, which closes much of the gap between Intel and Apple Silicon machines for images that have no arm64 build. The toggle lives under Features in development in 4.22.

My own Docker Desktop v4.22.0.
With it on, you can drop the explicit platform flag from commands like:
docker run --platform=linux/amd64What this means in practice
Every 2023 release chipped at the same problem: the gap between running a container on your laptop and running it the way production does. On macOS, the gVisor networking switch alone justifies the upgrade from anything before 4.19, and VirtioFS as the default makes bind-mounted builds usable again. Those are Docker’s claims and Ajeet’s measurements, not mine, and the next section is where my own numbers go.
My 2026 rerun
Three years on, I reran the commands above on my own machine so that this post carries at least one set of numbers that are mine. Raw outputs, the scripts, and every adaptation are published at /benchmarks/docker-desktop-2026-10/.
Machine: Apple M4 Pro, 14 cores, 48 GiB, macOS 26.5.2. Docker Desktop 4.90.0 (engine 29.7.2, Linux kernel 7.0.12-linuxkit, aarch64), VM at 14 CPUs and 7.7 GiB. File-sharing implementation: VirtioFS (a bind mount inside a container reports type virtiofs); the mode was not switched during the run. A five-container Compose stack was running throughout. Each benchmark ran three times; the table gives the median and the range. The hardware and the Docker Desktop versions differ from everything measured in 2023, so the comparison shows direction, not a like-for-like regression test.
| Benchmark | My result, October 2026 | The 2023 figure it sits next to |
|---|---|---|
Container start, hyperfine … 'docker run -it --rm ubuntu echo' | 16.2 ms mean (15.9–16.5 ms; 13.4–24.6 ms across 68–81 runs each) | 44.3–46.5 ms on 4.22, 56.7–59.8 ms on 4.11 (Ajeet Singh Raina, Collabnix) |
docker cp of a 1 GB file from a bind-mounted directory | 5.27 s (5.24–5.32 s) | 6.75 s on 4.22 (Ajeet Singh Raina, Collabnix) |
docker cp of a 10 GB file, same setup | 54.2 s (52.9–54.6 s) | 1 min 04–06 s with VirtioFS on 4.22 (Ajeet Singh Raina, Collabnix) |
Redis clean build over a bind mount, time make in ubuntu | 1 min 52.6 s (1 min 51.6 – 1 min 53.1 s) | about two minutes on 4.21, seven on 4.20 (Docker, 4.21 release post) |
| pgbench, scale 10, 10 clients × 10 transactions, PostgreSQL 18.6 | 8,470 tps (8,313–8,635) | no sourced 2023 figure |
| iperf3 container → container | 95.6 Gbit/s (95.0–96.4) | no sourced 2023 figure |
| iperf3 container → host | 8.73 Gbit/s (8.71–8.74) | 19 Gbit/s on an M1 Mac mini (Docker, “Unleash Docker Desktop 4.22”); Docker’s post states neither the iperf3 parameters nor the direction, so the two numbers were not measured the same way |
| iperf3 host → container, published port | 10.5 Gbit/s (10.4–10.6) | no sourced 2023 figure |
Engine restart, docker desktop stop then start to first docker info | 2.27 s (2.24–2.37 s) | “four seconds on Apple Silicon” (Docker, 4.21 release post) |
What had to change to run this in 2026: the iperf3 image is networkstatic/iperf3, since Docker’s internal dockerpinata/iperf3:2.1 is not public; postgres:alpine now resolves to PostgreSQL 18, which refuses a data volume at /var/lib/postgresql/data, so the volume sits at /var/lib/postgresql and the client runs from the server image; the Redis clone lives under my home directory rather than /tmp; startup is timed from docker desktop start to the first successful docker info because the 2023 table named no method.