Architecture¶
One static Windows binary, one disposable distro, one raw byte stream.
Windows side (dockup.exe, Go, single static binary)
─────────────────────────────────────────────────────
docker.exe (user-installed, NOT bundled)
│ npipe:////./pipe/dockup_engine (HTTP-over-pipe, hijack-capable)
▼
dockup foreground (`dockup`) OR dockup daemon child (`dockup __serve`)
│ go-winio ListenPipe, per-connection goroutine
▼ wsl -d dockup -u root -- socat STDIO UNIX-CONNECT:/var/run/docker.sock
WSL side (distro "dockup", Debian 12, systemd=true)
─────────────────────────────────────────────────────
systemd (PID 1) → containerd.service → docker.service
│ unix:///var/run/docker.sock
▼ socat bridge (one per client conn, spawned by Windows side)
containers
Design decisions¶
- Raw byte-copy, no HTTP parsing. Hijacked streams (
run -it,exec,logs -f, TTY resize) only survive a transparent proxy — anything that parses HTTP breaks them. - No TCP hop. WSL2 NAT loopback can't reliably reach a distro-bound
127.0.0.1, so the bridge speaks to the engine over socatSTDIOper connection instead. TCP exists only as an opt-in127.0.0.1listener on the Windows side. - Message-mode pipe. The listener uses
MessageMode: trueso a client's stdin close arrives server-side as EOF; the bridge then half-closes socat stdin and gives the container a 60s grace period to flush and exit. - Loud failures. If the backend dies before producing a byte, the client gets HTTP 500 instead of a silent empty reply, and the daemon log records why.
dockup_engine, notdocker_engine. CI runners and user machines already hold Docker Desktop's pipe — hijacking it would fake a pass.- Debian rootfs, not nocloud. The cloud
disk.rawtarballs failwsl --import; the debuerreotype rootfs tarballs (same bookworm content) import cleanly. - Single source of truth.
installedlives only in%APPDATA%\dockup\state.json(locked read-modify-write);~/.dockup/config.jsonholds user intent only. Old config files carrying the key still load — the key is ignored and dropped on next save. - systemd reboot. One
wsl --terminate dockupafter writingwsl.confso systemd becomes PID 1; every unit (re)start is preceded bysystemctl reset-failed(start-limit throttling) and followed by aWaitDaemonpoll (boot race). Install scripts end withapt-get cleanbecause a dynamic VHDX only grows.