If you run Linux containers on Windows, you probably know this list:
-
vmmemstill holds a gigabyte or two of RAM long after your containers have stopped. - You delete 5 GB of images and your disk doesn't get any of it back, because the VHDX only grows. Getting the space back means compacting it by hand or opting into a sparse VHD.
- A project bind-mounted from
C:\is slow, and file watchers sometimes don't fire. - At a company with 250+ employees or $10M+ revenue, Docker Desktop needs a paid subscription.
None of these are bugs you can report and wait out. They follow from the design: Docker Desktop runs on WSL 2, and WSL 2 is a general-purpose Linux VM that a container engine happens to share.
So I started building something narrower. hyperbutler is an open-source (Apache-2.0) Docker engine for Windows 11. It boots a minimal Linux VM straight through the Windows Host Compute Service (HCS, the same API WSL 2 uses underneath), runs dockerd inside it, and exposes the Docker API on a Windows named pipe. No WSL. You keep using the stock docker CLI and Docker Compose; hyperbutler just adds a hyperbutler docker context.
hyperbutler start
docker run --rm hello-world
docker run -d -p 8080:80 nginx; curl.exe http://127.0.0.1:8080/
hyperbutler stop
It is a preview. I'll get to what that means at the end.
The numbers, and what they don't say
I installed both on the same laptop and measured them one at a time with the same script, each with its out-of-the-box settings. The machine: Windows 11 Home, Intel Core Ultra 7 155H, 32 GB RAM. Measured on 2026-11-02.
| hyperbutler | Docker Desktop 4.97 (WSL 2) | |
|---|---|---|
| Needs WSL | no | yes |
| Download | 116 MB | 592 MB (+351 MB WSL) |
| Installed size | 179 MB | about 6.7 GB |
| Start until a container runs | 2.5 s | 5.6 s |
| Idle memory, VM running | 322 MiB | 1.7–2.7 GB |
| Idle memory after Docker Desktop's Resource Saver stops its VM | n/a | 404 MiB (VM off) |
| Disk 5 min after deleting ~5 GB of images | 1.5 GiB, given back automatically | 6.8 GiB, kept |
| VM memory 2 min after a 2 GiB workload | 320 MiB | 1.3 GB |
Bind mount from C:\: stat / read / write 2000 small files |
0.04 / 0.4 / 1.8 s | 2.3 / 3.4 / 4.9 s |
| License | Apache-2.0 | paid subscription for companies with 250+ employees or $10M+ revenue |
Now the caveats, because they matter:
- One machine, one build of each. This is not a general result. I haven't measured Windows 11 Pro, other hardware, or other versions.
-
Defaults on both sides. By default WSL gives Docker Desktop's VM half the RAM (16 GB here) and all 22 logical processors. I didn't test Docker Desktop with a smaller VM set in
.wslconfig, or hyperbutler with a bigger one. - Resource Saver is fair to mention. After about 7 minutes without containers, Docker Desktop stops its VM, and its host processes then use 404 MiB. The cost is that the next command has to wait for the VM to boot again, and I didn't measure how long that takes.
-
"Until a container runs" is the start measure that counts. Docker Desktop's backend answers
docker infoeven while its VM is stopped, so timingdocker infoalone would flatter it. -
Not measured at all: network throughput against Docker Desktop,
docker build, GPUs.
Docker Desktop is also well ahead on breadth: Kubernetes, Windows containers, extensions, a mature GUI, Scout and build cloud, support contracts. hyperbutler runs Linux containers with the Docker CLI and Compose, and that's it. If you need any of those other things, this isn't for you yet.
The full method, raw CSVs and the list of what wasn't measured are in docs/compare-docker-desktop.md.
How it works
There are three parts, and each gets as little privilege as its job needs:
Windows, your account (no admin)
+-----------------------------------------------------------+
| docker CLI / Compose / SDKs |
| | named pipe: \\.\pipe\docker_engine_hyperbutler |
| v |
| hyperbutler engine (hyperbutler.exe, runs as you) |
| Docker API relay, user-mode network (NAT, DNS, ports), |
| bind-mount file server, disk-space guard, CLI, MCP |
+-----------+-------------------------------+---------------+
| start / stop / status | Hyper-V sockets
v | (API, network, files)
+---------------------------+ |
| hyperbutler broker | |
| Windows service, SYSTEM | |
| only part that calls HCS | |
| 7 typed requests | |
+-----------+---------------+ |
| HCS |
v v
+-----------------------------------------------------------+
| Linux VM on Hyper-V (no WSL) |
| hyperbutlerd guest agent -> dockerd + containerd |
| read-only, hash-pinned system disk |
| sparse data disk that gives freed space back |
+-----------------------------------------------------------+
-
The engine is a normal process running as you. It relays the Docker API from the named pipe into the VM. It runs the VM's network in user mode (gVisor netstack), so there's no virtual switch, no vEthernet adapter, no WinNAT. It serves bind mounts from
C:\over FUSE, and it pauses the VM before your host disk fills up. - The guest is a minimal Alpine userland on a read-only system disk, pinned by hash. Its data disk is sparse, and the guest hands freed blocks back to Windows. That's the whole reason the disk row above reads 1.5 GiB instead of 6.8.
- The broker is the only privileged piece, and it's the one I thought about most.
Why a SYSTEM service, and why so small
Windows lets only administrators and members of the Hyper-V Administrators group use HCS. The easy fix is to add your account to that group. But Microsoft documents that group as having complete, unrestricted access to Hyper-V. Its members control VM configuration, which includes which host files a VM can open. And every program you run inherits that power, not just the tool that asked for it. Memberships like that also tend to outlive the tool that asked for them.
So the next release's MSI installs a small broker service instead (it is merged, and goes out with v0.1.0). It's the only hyperbutler component that calls HCS, and it was designed backwards from the bug classes behind earlier local privilege escalations in privileged container helpers on Windows: trusting caller-supplied paths, symlink and TOCTOU races, and named-pipe squatting.
- It accepts no paths, command lines or HCS JSON. There are seven short, typed requests (start, stop, status, resize within fixed limits, pause/resume, version, open your endpoint), and it builds the VM from a fixed template.
- It learns who you are from the pipe (the client's token), never from the message, and it refuses links and other users' folders.
- It opens the guest image only from the folder next to its own executable in Program Files, and checks it against a SHA-256 compiled into the binary.
- The connection that started the VM is the VM's lease: when it closes, the VM is terminated, so a crash doesn't leave a VM running unseen.
- The data plane (files, ports, the Docker API) never runs in the broker. It stays in your own unprivileged process.
Until that release, the current preview (v0.1.0-preview.1) still adds you to Hyper-V Administrators, and the zip, which has no service, will keep needing that membership as a documented developer mode.
Two things that surprised me
The VM that wouldn't give memory back, because of 2 MiB holes. The Hyper-V balloon driver's free page reporting only returns free blocks of order 9, which means 2 MiB. After a workload, about 70 MiB sat free in smaller blocks. Linux counted it as free, the host kept it, and the amount grew with every run. Setting page_reporting.page_reporting_order=0 on the guest kernel brought vmmem back to idle after every fill (+84 MiB down to about 0). If you've ever wondered why vmmem never quite comes back down, this is one of the reasons.
The same Hyper-V socket, much faster by changing who dials. All container traffic crosses one Hyper-V socket (hvsock) between the engine and the guest. It turns out Windows sends slowly on an hvsock it accepted: about 1 ms per ~600 KB, against about 50 µs on one it dialed. I couldn't find this documented anywhere. Swapping the roles, so the guest listens and the host dials, plus segmentation offload, took downloads into a container from 2.7 to 10.2 Gbit/s. (That's a development-harness measurement with a 2 GiB / 2 vCPU guest, not part of the Docker Desktop comparison.)
Letting an AI agent touch Docker, carefully
hyperbutler mcp is a Model Context Protocol server on stdio, so an MCP client such as your editor's AI assistant can see what the engine is doing:
{
"mcpServers": {
"hyperbutler": {
"command": "hyperbutler.exe",
"args": ["mcp"]
}
}
}
It is read-only by default: status, containers, logs, images, volumes, disk and memory use, settings, diagnose. --allow-write adds start, stop, run, pull and config_set. Deleting needs --allow-delete as well. A tool you didn't allow isn't listed and can't be called. Results go through the same redaction as hyperbutler diagnose and are bounded in size. It opens no network port, and it exits with its client. So "why is my container restarting?" is safe to ask, and "clean up my images" is something you have to opt into.
Where it stands, honestly
-
Preview.
v0.1.0-preview.1is released.v0.1.0stable waits on a 24-hour soak test (the 1-hour soak passed: 12,827 docker commands, none failed, no leak trend) and on an install test of the MSI's broker service on a clean machine. So far it has been installed, upgraded, repaired, rolled back and uninstalled on one machine. - Windows 11 only, x64. Home, Pro and Enterprise. Windows 10 isn't supported.
-
Unsigned builds. Until releases are code-signed, SmartScreen shows "Windows protected your PC". Click More info, then Run anyway. Check the download against the release's
SHA256SUMSfirst:
Get-FileHash .\hyperbutler-*-windows-amd64.msi -Algorithm SHA256
Every release file also has build provenance you can check with gh attestation verify <file> --repo Higangssh/hyperbutler.
-
Known gap: bind mounts from Windows paths work, but write-heavy trees aren't fast yet. Use a named volume for
node_modules. -
Side by side is fine. Docker Desktop can stay installed. hyperbutler serves its own pipe and context, and
docker context use desktop-linuxswitches back.hyperbutler import --from-desktopcan copy images and volumes over, and it leaves Docker Desktop as it was.
Try it
- Download the MSI from Releases. One UAC prompt sets up Virtual Machine Platform and the PATH; the current preview also adds you to Hyper-V Administrators, which the broker service replaces from v0.1.0. Sign out and back in if it asks.
- Restart if it asks, then run
hyperbutler startand your usualdockeranddocker composecommands. - If something breaks,
hyperbutler doctorchecks what this PC is missing, andhyperbutler diagnosebuilds a local zip for a bug report (nothing is uploaded).
What I need most right now is reports from machines that aren't mine: other CPUs, Windows 11 Pro and Enterprise, corporate proxies, big Compose projects. Please open an issue with what worked and what didn't. If you'd like to follow along, a star on GitHub helps. More at hyperbutler.dev.
Docker is a trademark of Docker, Inc. hyperbutler is not affiliated with or endorsed by Docker, Inc. or Microsoft.
Top comments (0)