FreeWilly puts upstream Moby into a WSL2 distribution it owns, serves the docker_engine pipe every tool you already have looks for, and shows your containers in one window. Per-user install, no administrator prompt, and nothing left running that you did not ask for.
freewilly read context~150 tokengine running wsl:freewilly api=v1.43 pipe=docker_engine ctx=default(ok) web up 6m healthy svc:shop/web :8080→80 listening db up 6m healthy svc:shop/db :5432→5432 listening api exited 137 ×3/2m svc:shop/api OOM limit=512m disk images 14G (4.2G dangling) volumes 2.1G (1 unused) compose ./docker-compose.yaml → shop 3 svc, 3 present cursor c:4f21a0
freewilly read doctor api~200 tokfreewilly read logs api --dedup --budget 1500 --out .freewilly/logs/api.log~300 tokfreewilly do compose up --wait~100 tokfreewilly read verify svc:shop/api~80 tokA scripted session — the costs are targets the benchmark must prove or falsify, acceptance criteria and not measurements. Steps 1, 2, 3 and 5 are reads, so one allowlist line — Bash(freewilly read:*) — removes every prompt on the inspection path while step 4 still asks.
FreeWilly is a Docker installation whose primary operator is a coding agent. The agent runs, inspects and diagnoses; you install, approve and intervene. Every decision on the agent surface is judged in tokens rather than clicks — which is why the site opens on a session and not a screenshot.
The desktop path is not sacrificed — the installer, the tray and the window are what it is for, and they landed before the agent surface did rather than after.
Binding, in the same sense as the product's. A feature that breaks one is wrong even if it was asked for. Each names the defect it prevents.
Every agent-facing capability is a freewilly CLI verb first. An MCP tool is a second head over the same method, or it does not exist.
Learning what the machine is running is a product feature, not a docs problem. Needing six commands to learn the engine's state is a defect in FreeWilly.
Every response has a size ceiling and the canonical task has a measured cost. “It got cheaper” has to be a number, and a regression fails the build.
An unbounded log read is the largest token sink here. Write it to disk and let the agent Grep it — it pays for the lines that match, not for the whole log.
A 64-hex id changes on every recreate. The address is the name — svc:<project>/<service>, or the container name — so ids stop being currency threaded across calls.
Every refusal carries what was wrong, what is allowed, the nearest match, a correct example — and the Windows fact that explains it. An error that costs a round trip to read is a defect.
Read and write are split at the argv level so an allowlist can tell them apart. Destructive calls take a confirm token, and everything the agent creates is labelled with its session.
Give it cheap textual proof that what it started works — the port listens from Windows, the mount resolved, the service answered — or every mistake costs a trip back to you.
A cursor and a change feed, so a follow-up session reads the delta rather than re-deriving the whole machine from nothing.
The surface is a shape over the Engine API and facts Windows already knows. It is not a second Docker CLI and never grows a build, a push or a compose up of its own.
Moby and the docker CLI are Apache-2.0 and always were. What a company pays for, and what a managed laptop cannot install, is the wrapper around them. FreeWilly is a different wrapper: it installs the same engine, gets out of the way, and stops when you tell it to.
Free at any headcount, for any use, with no seat maths and no renewal date. That is the whole reason to try this instead of the thing everyone already has.
No Windows service, no scheduled task, and autostart is off until you turn it on. The engine runs for exactly as long as something is running it — an engine that holds gigabytes from every boot is the complaint this project starts from.
Everything lands under %LOCALAPPDATA%\FreeWilly: the verified downloads, the distribution's disk, and the docker.exe that goes on your PATH. Which is what reaches a corporate laptop you are not an administrator on.
Alpine's minirootfs, Docker's own static Linux binaries, Docker's own Windows CLI — each pinned to a version and a SHA-256 this project states itself. Nothing is patched, so nothing here can be blamed for how the engine behaves.
A preflight reads the Windows build, virtualization, the WSL2 kernel and any rival engine, and refuses the install while a blocking row is not green. Nothing is written to disk while the answer is no.
The distribution is called freewilly and it is this tool's, never yours. Your own WSL distros are untouched, and removing the engine cannot take anything of yours with it.
“It does not work on my machine” has five common causes on Windows, and they have five different remedies. freewilly --preflight names the one you have, prints the command that fixes it, changes nothing, and exits 1 so an installer can stop rather than fail halfway.
FreeWilly preflight — what this machine can host [ok ] Windows build Windows 10.0, build 26200 [ok ] Hardware virtualization enabled — a hypervisor is already running [FAIL] WSL2 not installed — wsl.exe is not on this machine -> Run `wsl --install --no-distribution` in an administrator terminal, then reboot. [ok ] Container engine nothing else owns the docker command or the docker_engine pipe 1 row blocks an install. Nothing has been copied to disk.
The same check runs inside the installer, and the same report is what a clean Windows 11 virtual machine is driven through on the way to a release — because a red row nobody has ever executed is not a check.
Provisioning runs from an installer, where there is no terminal to answer a prompt in. So every step is unattended, every step is named, and the run stops at the first failure — a report listing six failures where there was one is a report nobody can act on.
Five of the eleven steps, one per artefact: the Alpine root filesystem, the static Linux engine, the Windows CLI zip, and the Compose and Buildx plugins — each downloaded and checked against a digest recorded in this repository, not one served by the same host as the file. Already verified on disk means no second download.
The engine tarball's member list is read locally: do the entries share one top directory, and are dockerd, containerd, runc and the rest actually in there? A bad archive is caught before a distribution exists.
wsl --import freewilly … --version 2. A fixed name that is this tool's, so an apt upgrade or a wsl --unregister you ran for your own reasons cannot take the engine with it.
One non-interactive sh script under set -e, so it stops where it broke: iptables and socat from apk, the binaries unpacked into /usr/local/bin, and systemd=false — nothing here is a service.
docker.exe is extracted to …\FreeWilly\bin. Putting that directory on your PATH is the installer's job, and this step's only job is being the path it points at.
Both plugins go where the CLI looks for one, so docker compose up and docker buildx build work from the install rather than from a second download — pinned by digest like everything else here. Every build the daemon kept is a page in the window, which is also where the docker-desktop:// link buildx prints at the end of a build lands.
--plan prints every pinned version, digest and path and reaches nothing at all. --acquire downloads and verifies, and stops before WSL2 is touched — both change nothing outside this tool's own directory.
freewilly — put upstream Moby into a WSL2 distribution this tool owns.
--plan the pinned versions, digests and paths; reaches nothing
--acquire download and verify every artefact, and stop
--provision acquire, import the distribution, install the engine
--run start the engine and serve \\.\pipe\docker_engine until Ctrl+C
--stop stop the engine and terminate the distribution
--status what the engine is doing, by asking it
--api version and containers, read through the Engine API
--watch print /events as they happen, until Ctrl+C
--autostart on | off | status - off unless you turn it on
--help this
Exit code 0 means the mode finished; 1 names the step it stopped at. For --status, 1 means the engine is not answering.docker commands do not need to knowA Linux dockerd cannot create a Windows named pipe — that is a Win32 object. So something on the Windows side has to, or every shell and every script you already have needs a DOCKER_HOST. FreeWilly is that something.
The pipe is created for your account and nobody else. A forwarded port cannot say that, and full access to the Engine API is full access to the machine — so the hop runs over wsl.exe's stdio instead.
It is the same pipe name, so the CLI, Compose, Testcontainers, an IDE plugin and whatever your CI script does locally all find it without a setting. Nothing has to be told about FreeWilly.
The app talks HTTP over the pipe directly — a named-pipe stream handed to .NET's own HTTP handler, pinned to Engine API v1.43. No NuGet package, and no shelling out to docker.exe once per refresh.
The icon carries the engine state as a shape, and colour only reinforces it. At sixteen pixels a hue is a hint: two of these are seen at a glance, by people who may not separate red from green, against a taskbar that is light on one machine and dark on the next. These three are still distinguishable in a screenshot printed in black and white.
A filled disc. And it means the engine answered — the state comes from the event stream's own connection, not from remembering that a start was clicked.
A ring with a bite out of it: the same outline as stopped and unmistakably not it, which is what tells “on its way up” from “not running” without relying on hue.
A plain ring. Start engine is one click away in the menu, and it launches the engine in a process the tray does not own.
Because that is what the tool is opened for. Name, image, state, how long it has been up, and the ports — and the ports are links, because a published 8080 is the thing you actually wanted and retyping localhost:8080 is a small daily tax a GUI exists to remove.
ThemeMode="System" — light and dark follow the OS, with no extra package./events as the daemon writes it, and only the events that change a container list cost a read — the rest would be a poll in disguisedocker run in any shell appears without you touching the windowhttp://localhost:x is not where a published UDP port isdocker-desktop:// link buildx prints at the end of every build lands, because the record it names is one the daemon kept and only the address was deadEach section above has a page to link at — from a README, an issue, or a search result.
The five common causes of a Docker failure on Windows, each with the command that fixes it, read-only and exit 1 while a blocking row is not green.
Read the page →Eleven unattended steps that stop at the first failure: acquire and verify five artefacts by digest, inspect the archive, import an owned WSL2 distro, install inside it, and place the CLI with its Compose and Buildx plugins.
Read the page →A Linux dockerd cannot create a Windows named pipe, so FreeWilly serves docker_engine and forwards over wsl.exe stdio. The ACL grants your account and nobody else — which a forwarded port cannot express.
Read the page →One window that is the list of containers, with ports as links; acting on a container; logs and a shell for what the state does not say; and images and volumes made legible.
Read the page →One budgeted context pack answers a session's opening questions; read doctor is the diagnostic join; and every refusal carries the Windows fact that explains it.
Read the page →Five things this project has decided against, written down where they can be pointed at. A tool with no stated non-goals is a tool that will eventually be asked for all of them.
Kubernetes, extensions, a dashboard for everything. The list a user actually opens the app for is short, and stopping there is what keeps this small enough to trust.
Upstream Moby, unpatched, pinned by digest. If the engine misbehaves, that is between you and upstream, and this project has not touched it.
Linux already has the engine, and the entire mechanism here is WSL2 and a Windows named pipe. Portability would mean a different tool wearing the same name.
Nothing is measured, nothing is sent, and there is nothing to register. There is no build of this with an opt-out.
The complaint that sends people looking for an alternative is an engine holding gigabytes from every boot. Not being that is the product, not a preference.
The release page carries the installer and the SHA256SUMS.txt to check it against, and nothing else to choose between. It installs into %LOCALAPPDATA%\FreeWilly for your account alone — which is what reaches a managed corporate laptop, the machine this project exists for. The WSL2 feature the engine needs may still want elevation of its own; the installer runs the preflight and says so rather than failing halfway through a download.
Uninstalling removes what was installed and asks about what was created. The freewilly distribution holds every image, container and volume you have, so it is never deleted without a question — and an unattended uninstall keeps it.