Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

Windows in a Docker Container — Admin Guide

Generated June 2026 from dockur/windows at 0c9472b

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

This guide documents the architecture, internals, configuration surface, and operational concerns of dockur/windows for administrators, DevOps engineers, and developers who deploy or extend it. It is the companion to the User Guide, which covers installation and everyday use.

dockur/windows is a thin, Windows-specific layer on top of the qemux/qemu base image. It contributes the logic to acquire Windows installation media, inject virtio drivers and an unattended-setup answer file, rebuild a bootable ISO, and share files back to the host. The heavy lifting of disk creation, networking, display, and the QEMU command line lives in the base image.


1. Executive Summary

Aspect Summary
What it is A Docker image that runs Windows in a QEMU/KVM virtual machine, with fully unattended installation
Base image qemux/qemu:7.32 (provides the VM runtime, disk/network/display, web viewer)
This repo's role Windows media download + driver injection + unattended answer file + ISO rebuild + Samba share + graceful shutdown
Language Bash (six scripts in src/), plus per-version unattend XML assets
Entry point /usr/bin/tini -s /run/entry.sh
Acceleration Requires host KVM (/dev/kvm); x64 only
Exposed ports 8006 (web viewer / noVNC), 3389 (RDP, TCP+UDP)
Persistent state /storage volume (virtual disk + marker files)
Licensing MIT-style open source; no Microsoft media redistributed (downloaded at runtime)

Note

This is an x64 image. The ARM64 variant is a separate image, dockurr/windows-arm, referenced as a multi-arch build target in the Dockerfile (FROM dockurr/windows-arm:${VERSION_ARG} AS build-arm64).


2. Architecture

2.1 High-level layout

            Host (Linux with KVM)
  ┌───────────────────────────────────────────────┐
  │  Docker / Podman / Kubernetes                  │
  │                                                │
  │   ┌──────────────  Container  ──────────────┐  │
  │   │  tini → /run/entry.sh                    │  │
  │   │     │                                    │  │
  │   │     ├─ source base-image scripts ────────┼──┼─ from qemux/qemu:7.32
  │   │     │   (start, utils, reset, server,    │  │   (disk, network, display,
  │   │     │    disk, display, network, boot,   │  │    web viewer, QEMU args)
  │   │     │    proc, memory, balloon, config,  │  │
  │   │     │    finish)                         │  │
  │   │     │                                    │  │
  │   │     ├─ source THIS repo's scripts ───────┼──┼─ src/*.sh
  │   │     │   define · mido · install · samba  │  │
  │   │     │   power                            │  │
  │   │     │                                    │  │
  │   │     └─ exec qemu-system-x86_64 ──────────┼──┼─ the Windows VM
  │   │            ├─ /dev/kvm  (acceleration)   │  │
  │   │            ├─ virtio disk → /storage     │  │
  │   │            ├─ virtio-net → bridge/macvlan │ │
  │   │            └─ VGA/PTY → noVNC :8006       │  │
  │   └──────────────────────────────────────────┘  │
  └───────────────────────────────────────────────┘
   Browser :8006 (install/view)   RDP client :3389 (daily use)

2.2 Boot orchestration (src/entry.sh)

entry.sh is the container entry point. It sets four defaults, then sources a fixed ordering of shell modules — most from the base image, the Windows-specific ones from this repo — before launching QEMU:

: "${APP:="Windows"}"
: "${PLATFORM:="x64"}"
: "${BOOT_MODE:="windows"}"
: "${SUPPORT:="https://github.com/dockur/windows"}"

. start.sh      # Startup hook            (base image)
. utils.sh      # Load helper functions   (base image)
. reset.sh      # Initialize system       (base image)
. server.sh     # Start web server        (base image)
. define.sh     # Define versions         (THIS repo)
. mido.sh       # Download Windows        (THIS repo)
. install.sh    # Run installation        (THIS repo)
. disk.sh       # Initialize disks        (base image)
. display.sh    # Initialize graphics     (base image)
. network.sh    # Initialize network      (base image)
. samba.sh      # Configure Samba         (THIS repo)
. boot.sh       # Configure boot          (base image)
. proc.sh       # Initialize processor    (base image)
. power.sh      # Configure shutdown      (THIS repo)
. memory.sh     # Check available memory  (base image)
. balloon.sh    # Memory ballooning       (base image)
. config.sh     # Assemble QEMU arguments (base image)
. finish.sh     # Finish initialization   (base image)

Each sourced script runs at source time (they are procedural, not function libraries), so the install pipeline in install.sh executes as it is sourced — before QEMU launches. After all modules load:

cmd=(qemu-system-x86_64)
info "Booting ${APP}${BOOT_DESC} using QEMU v$version..."

if [[ "$SHUTDOWN" != [Yy1]* ]]; then
  exec "${cmd[@]}" ${ARGS:+ $ARGS}
fi

When graceful shutdown is enabled (the default), QEMU is run in the background through a named pipe (qemu.pipe) so its serial output can be filtered with sed and tee'd to the PTY. A 30-second watchdog (boot) checks that the VM started, and on exit the finish handler (in power.sh) cleans up.

Important

Because base-image scripts are sourced by name with no path, the container's working directory is cd /run and all scripts (this repo's src/ plus the base image's) are copied to /run/. This is set in the Dockerfile: COPY --chmod=755 ./src /run/.

2.3 The Windows-specific modules

Module Responsibility
define.sh Normalizes the VERSION and LANGUAGE inputs; holds the version-ID tables, the language/culture map, edition naming, and the mirror URL/hash/size tables; sets per-version QEMU machine quirks (setMachine)
mido.sh Acquires media: the Microsoft consumer/eval download APIs, the ESD catalog path, and the four numbered fallback mirrors; verifies checksums
install.sh The install pipeline: detect custom ISO, download, extract, detect version/language, inject drivers + answer file, rebuild bootable ISO, persist settings
samba.sh Configures and starts Samba (and wsdd/NetBIOS) to expose the host share inside Windows
power.sh Traps signals and performs a graceful ACPI shutdown of the guest; the finish cleanup handler
entry.sh Sources everything and launches QEMU

3. The Installation Pipeline (Core Internals)

The heart of this project is src/install.sh. Its driver section runs this sequence (each step exits with a distinct code on failure — see Section 7):

parseVersion        normalize VERSION  → canonical id (e.g. "11" → "win11x64")
parseLanguage       normalize LANGUAGE → culture code
detectCustom        look for a user-supplied /custom.iso or /boot.iso
startInstall        decide whether install is needed; back up prior install
  └─ if already installed → bootWindows() (reload saved settings) and run
downloadImage       fetch ISO/ESD from MS or a mirror (mido.sh)
extractImage        unpack ISO (7z) or apply ESD (wimlib) into a work dir
detectImage         identify Windows version + language from install.wim
prepareImage        per-version boot setup (etfsboot/efisys or legacy boot.img)
updateImage         inject virtio drivers + the unattend XML answer file
removeImage         delete the source ISO (unless custom)
buildImage          rebuild a bootable ISO with genisoimage
finishInstall       persist marker + settings files into /storage

3.1 Version normalization (define.sh)

parseVersion() maps a wide set of human aliases to a canonical internal ID. For example, all of 11, 11p, win11, pro11, windows 11 map to win11x64. Variant suffixes encode edition and channel:

Suffix pattern Meaning
-enterprise-eval Enterprise Evaluation media
-enterprise-iot-eval IoT Enterprise LTSC
-enterprise-ltsc-eval LTSC channel
-hv Hyper-V Server
nano11 / core11 / tiny11 / tiny10 Community trimmed builds (with a DETECTED base for the answer file)

A parallel getLanguage() table maps language inputs to a short code, display name, and BCP-47 culture (e.g. de-DE). parseLanguage() rejects anything not in the table (exit 56).

3.2 Media acquisition (mido.sh + define.sh)

The container tries multiple sources in order, controlled by downloadImage():

  1. Custom URL — if VERSION starts with http, download it directly (two attempts with a retry delay).
  2. Mido / Microsoft API (isMido → getWindows):
    • Consumer Win11 (download_windows): scrapes microsoft.com/.../software-download/windows11, obtains a product-edition ID, passes the Microsoft "session protection" handshake (vlscppe.microsoft.com/tags, ov-df.microsoft.com), then requests a SKU and a signed ISO link.
    • Evaluation / Server (download_windows_eval): parses the Microsoft Evaluation Center page and follows the go.microsoft.com/fwlink redirect.
  3. ESD catalog (isESD → getESD): for Win10/11 Pro and Enterprise, downloads the Microsoft ESD product catalog, filters by architecture/edition/language with xmllint XPath, and extracts the ESD URL, SHA-1, and size.
  4. Fallback mirrors (getLink1..getLink4, MIRRORS=4): static URL/hash/size tables pointing at dl.bobpony.com, files.dog/MSDN, nixsys.com/drivers, and archive.org/download. Used when the Microsoft path is unavailable or for legacy versions.

verifyFile() checks the downloaded file's SHA-256 (or SHA-1 for ESD) against the table when VERIFY is enabled, and warns on size mismatch.

Note

The Microsoft download path deliberately mimics a real browser: get_agent() computes an approximate current Firefox version from the system clock, and the curl calls pin --tlsv1.2 --http1.1 with the Accept header removed. This is required to pass Microsoft's automated-download protection.

3.3 Image extraction (install.sh)

extractImage() branches on file type:

  • .esd → extractESD() uses wimlib-imagex to apply the install image, export WinPE + Setup into boot.wim, then export the selected edition into install.wim (LZMS-compressed). A free-space pre-check requires ~9.6 GB.
  • .iso → 7z x unpacks the ISO; the volume label is read with isoinfo. If UNPACK=Y (set when the download was a compressed archive), a nested ISO is extracted first.

3.4 Version & platform detection (install.sh)

detectImage() reads install.wim/install.esd metadata (wimlib-imagex info -xml, converted from UTF-16LE):

  • checkPlatform() compares the image architecture (ARCH 0=x86, 9=x64, 12=arm64) against PLATFORM; a mismatch exits 67 ("You cannot boot ARM64 images on a x64 CPU").
  • detectVersion() → selectVersion() maps DISPLAYNAME/PRODUCTNAME/NAME to an internal ID and prefers Enterprise → Ultimate → base edition that has a matching assets/<id>.xml answer file.
  • detectLanguage() reads the default language from the WIM XML.
  • The matching answer file is chosen by setXML(), which searches /custom.xml, $STORAGE/custom.xml, /run/assets/custom.xml, the per-version asset, then a base fallback. If none is found, the install falls back to manual mode.

3.5 Driver and answer-file injection (install.sh)

updateImage() modifies the extracted media before the ISO is rebuilt:

  • addDrivers() extracts the bundled virtio-win driver archive (/var/drivers.txz, version pinned by the Dockerfile VERSION_VIRTIO ARG) and, via addDriver(), copies the per-OS driver set (qxl, viofs, NetKVM, viostor, vioscsi, Balloon, pvpanic, vioinput, viogpudo, vioserial, etc.) into $WinPEDriver$ and the $OEM$ tree using wimlib-imagex update.
  • The selected assets/<id>.xml answer file is copied in and patched by updateXML() — resolution, UI/locale/keyboard culture, username, a base64-UTF16 encoded administrator password, server edition, and an optional product KEY.
  • For legacy Windows (2000/XP/2003), prepareInstall() performs a very different flow: it edits TXTSETUP.SIF with sed to register viostor/iaStor SCSI/SATA drivers, writes a WINNT.SIF unattend file, an install.reg, an install.vbs (renames the built-in Administrator and creates a desktop share shortcut), and a cmdlines.txt. Generic, official Microsoft trial keys are embedded for XP/2003 only when no key is supplied.

3.6 ISO rebuild & persistence (install.sh)

buildImage() calls genisoimage with mode-specific flags:

  • UEFI (non-legacy): El Torito + UDF, dual boot catalog (etfsboot.com + efisys_noprompt.bin).
  • Legacy (2k/XP/2003, 9x, others): BIOS boot images with reduced ISO levels.

finishInstall() then writes a set of marker/settings files into /storage so the VM reboots with consistent hardware on subsequent runs. bootWindows() reads them back when an install already exists:

File in /storage Holds
windows.boot Marker that installation completed
windows.base Basename of the prepared ISO
windows.ver Image build version (/etc/version)
windows.mode BOOT_MODE (e.g. windows_legacy, windows_secure)
windows.old Legacy QEMU MACHINE type
windows.args Extra QEMU ARGS
windows.vga / windows.usb / windows.net / windows.type Non-default VGA, USB controller, network adapter, disk type

A magic first byte (0x16, or 0x17 for manual installs) is written into the ISO to mark it as already processed, so skipInstall() can short-circuit on later boots.

Important

Windows 11 unattended installs force BOOT_MODE=windows_secure (Secure Boot + TPM) when required, and multi-socket systems are also switched to secure boot to work around a known freeze. These decisions are persisted in windows.mode.


4. Configuration Reference

All configuration is via environment variables. Defaults marked (Dockerfile) are set as ENV in the image; the rest default inside the scripts.

4.1 Core variables

Variable Default Source Purpose
VERSION 11 Dockerfile Windows edition or custom ISO URL
RAM_SIZE 4G Dockerfile Guest memory (handled by base image)
CPU_CORES 2 Dockerfile Guest vCPUs (handled by base image)
DISK_SIZE 64G Dockerfile Main virtual disk size (base image)
DISK2_SIZE / DISK3_SIZE (unset) base image Additional virtual disks
USERNAME Docker install.sh Account created at install
PASSWORD admin install.sh Password for that account
LANGUAGE en define.sh Display language of downloaded edition
REGION (culture) install.sh User locale (UserLocale/SystemLocale)
KEYBOARD (culture) install.sh Input locale
KEY (unset) install.sh Product key injected into the answer file
EDITION (unset) define.sh Server edition (Standard/Datacenter/Core)

4.2 Install behavior

Variable Default Purpose
MANUAL (unset) Y skips the unattended answer file; manual Setup
VERIFY (unset) Y verifies the downloaded ISO checksum
REMOVE (unset) N keeps the installation ISO after setup
WIDTH 1280 Display width baked into legacy answer files
HEIGHT 720 Display height baked into legacy answer files
ARGUMENTS (unset) Raw extra QEMU arguments (escape hatch)

4.3 Networking

Variable Default Purpose
DHCP (unset) Y gives Windows its own router-assigned IP (needs /dev/vhost-net)
ADAPTER (per version) Guest NIC model; persisted in windows.net
NETWORK (base image) Networking backend (bridge, macvlan, passt, slirp)

4.4 File sharing (Samba)

Variable Default Purpose
SAMBA Y Enable the Samba host share
SAMBA_DEBUG N Stream Samba/wsdd logs
SAMBA_INTERFACE (unset) Extra interface to bind Samba to

4.5 Shutdown

Variable Default Purpose
SHUTDOWN Y Enable graceful ACPI shutdown trapping
TIMEOUT 115 Seconds to wait for clean guest shutdown
QEMU_TIMEOUT (unset) Overrides TIMEOUT if set
DEBUG (unset) Verbose pipeline logging

4.6 Volumes, devices, ports

Mount / device Role
/storage Virtual disk + marker files (declared VOLUME in Dockerfile)
/storage2, /storage3 Backing for DISK2_SIZE/DISK3_SIZE
/shared, /shared2, /shared3 (or /data, /data2, /data3) Host folders exposed inside Windows as Data/Data2/Data3
/oem Copied to C:\OEM; install.bat runs at end of setup
/boot.iso or /custom.iso Bind a local ISO to install instead of downloading
/custom.xml Override the unattended answer file
/disk1 (device) Pass-through main disk (formatted)
/disk2+ (device) Pass-through secondary disks (untouched)
/dev/kvm Required — KVM acceleration
/dev/net/tun Required — container networking
/dev/vhost-net Required only for DHCP=Y
Port 8006/tcp Web viewer (noVNC)
Port 3389/tcp+udp RDP
Port 5900/tcp VNC (exposed in the Kubernetes manifest)

5. Deployment

The canonical compose.yml is shown in the User Guide. Key production notes:

  • Always bind /storage to a host path or named volume.
  • Keep stop_grace_period: 2m so the ACPI shutdown completes.
  • restart: always brings the VM back after host reboots.

5.2 Kubernetes

The repo ships a complete kubernetes.yml with a PersistentVolumeClaim (64Gi), a Deployment, and a Service:

securityContext:
  capabilities:
    add:
    - NET_ADMIN
  privileged: true

/dev/kvm and /dev/net/tun are mounted from the host via hostPath: { type: CharDevice }, and the Service exposes ports 8006 (http), 3389 (rdp tcp+udp), and 5900 (vnc).

Caution

The Kubernetes manifest runs the pod privileged: true. This is the simplest way to grant device access but gives the pod broad host privileges. In multi-tenant clusters, replace it with a tightly scoped device plugin and the minimum capabilities, and schedule only onto nodes with /dev/kvm.

5.3 Other launch paths

The README also documents GitHub Codespaces and the third-party WinBoat graphical installer as front ends for the same image.


6. Operations

6.1 Startup and viewing

docker compose up -d
docker compose logs -f      # watch the install pipeline

Expected milestones in the log:

❯ Requesting Windows 11 Pro from the Microsoft servers...
❯ Downloading Windows 11 Pro...
❯ Extracting downloaded ISO image...
❯ Adding drivers to image...
❯ Adding autounattend.xml for automatic installation...
❯ Building Windows 11 Pro image...
❯ Booting Windows using QEMU v9.x.x...
❯ Windows started successfully, visit http://127.0.0.1:8006/ to view the screen...

6.2 Graceful shutdown (power.sh)

power.sh traps SIGTERM, SIGHUP, SIGABRT, and SIGQUIT. On signal, graceful_shutdown():

  1. Verifies the guest is past Setup (ready() — checks the PTY for the Windows Boot Manager line, or legacy "Booting from Hard").
  2. Sends system_powerdown over the QEMU monitor socket ($QEMU_DIR/monitor.sock) once per second.
  3. Escalates to SIGTERM near the end of the timeout window, then kill -9 in finish() if still alive.
  4. finish() removes the post-install CD-ROM ISO (unless REMOVE=N), kills helper daemons (Samba, wsdd, TPM, web, passt, dnsmasq, ballooning), and closes networking.

The timeout window is derived from TIMEOUT (minimum enforced; default 115 s), which is why stop_grace_period/--stop-timeout on the host must be at least as long.

Warning

If the host kills the container before the ACPI shutdown completes, Windows may mark the filesystem dirty and run chkdsk (or lose unsaved writes) on next boot. Keep the host stop timeout ≥ TIMEOUT.

6.3 Backups

/storage holds the entire machine. To back up, stop the container gracefully and copy the /storage folder. install.sh also auto-creates /storage/backups/<name> when it detects that VERSION or a custom ISO changed, preserving the previous windows.* data files before re-installing.

6.4 File sharing internals (samba.sh)

samba.sh writes /etc/samba/smb.conf with a guest-only, force user = root configuration (map to guest = Bad User, server min protocol = NT1 for legacy compatibility) and exposes the chosen share directory. It starts smbd, plus either nmbd (NetBIOS, for Windows 7 and older in legacy boot mode) or wsddn (Web Service Discovery, for Vista and newer). The share auto-selects /shared → $STORAGE/shared → /data → $STORAGE/data → a temp folder, and is reachable inside Windows at \\host.lan\Data.

Note

server min protocol = NT1 (SMB1) is enabled to support legacy guests. SMB1 is deprecated and insecure on real networks — acceptable here because the share is on an internal container interface, not exposed to the host LAN. Do not bridge this Samba instance onto a production network.


7. Exit Codes

install.sh uses distinct exit codes, which are valuable for diagnosing failed installs from container logs:

Code Stage Meaning
50 startInstall Could not create the temp working directory
56 parseLanguage Invalid LANGUAGE value
58 parseVersion (version normalization failure)
59 detectCustom Bind-mount maps to a non-existent file
60 abort / esd / unpack Aborted to manual install, or ESD/unpack path ended
61 downloadImage Could not download the ISO/ESD
62 extractImage Could not extract the image
63 updateImage Driver/answer-file injection failed
64 removeImage Could not remove the source ISO
65 buildImage genisoimage rebuild failed
66 prepareImage Boot-image preparation failed
67 checkPlatform / custom.xml Wrong CPU architecture, or /custom.xml bind missing
68 startInstall + bootWindows Neither install nor existing-boot path succeeded
69 finishInstall Could not persist install markers

mido.sh additionally maps every curl exit status to a descriptive message via handle_curl_error() (DNS failure, TLS error, timeout, IP-based block, etc.).


8. Security Analysis

Area Finding
Privilege Requires NET_ADMIN (and often privileged/device access). The container hosts a VM with raw device access — treat it as a privileged workload.
Default credentials Docker / admin are well-known defaults. Always override USERNAME/PASSWORD, and never expose port 3389 publicly.
RDP exposure Exposed RDP invites brute-force and account lockouts (community issue: locked accounts). Keep 3389 on a private network or behind a VPN.
Samba Guest-only, force user = root, SMB1 enabled. Safe on the internal container interface; never reuse this config on a real LAN.
Product keys Keys in define.sh/install.sh are official Microsoft generic trial/placeholder keys (no activation). The project distributes no copyrighted media.
Outbound network The image makes outbound HTTPS calls to Microsoft and mirror hosts to fetch media. In locked-down environments, pre-stage a local ISO via /boot.iso and avoid the download path entirely.
Browser-like fetch The Microsoft download handshake spoofs a Firefox user agent and replays Microsoft's anti-automation tokens. This is benign (it only retrieves the publicly available ISO) but will look anomalous to network monitoring.
macvlan isolation With macvlan, the host cannot reach the container — a containment benefit, but it also means host-based monitoring won't see the guest's traffic.

Important

The single most important hardening step is to change the default password and keep RDP off the public internet. The default Docker/admin pair combined with an exposed 3389 is the most common real-world exposure for this image.


9. Build & Dependencies

9.1 Image build (Dockerfile)

FROM scratch AS build-amd64
COPY --from=qemux/qemu:7.32 / /

RUN apt-get install samba wimtools dos2unix cabextract libxml2-utils libarchive-tools
    # + wsdd-native (VERSION_WSDD=1.24) installed from a .deb

COPY --chmod=755 ./src /run/
COPY --chmod=755 ./assets /run/assets
ADD https://github.com/qemus/virtiso-whql/releases/.../virtio-win-${VERSION_VIRTIO}.tar.xz /var/drivers.txz

VOLUME /storage
EXPOSE 3389 8006
ENV VERSION="11" RAM_SIZE="4G" CPU_CORES="2" DISK_SIZE="64G"
ENTRYPOINT ["/usr/bin/tini", "-s", "/run/entry.sh"]
Build ARG / dependency Pin Role
qemux/qemu 7.32 Base VM runtime image
VERSION_VIRTIO 1.9.57 virtio-win WHQL driver bundle (/var/drivers.txz)
VERSION_WSDD 1.24 wsdd-native for Web Service Discovery
samba apt SMB host file sharing
wimtools (wimlib-imagex) apt WIM/ESD image manipulation
cabextract apt Extract Microsoft .cab catalogs
libxml2-utils (xmllint) apt XPath over the ESD product catalog
libarchive-tools (bsdtar) apt Extract the driver archive
dos2unix (unix2dos) apt CRLF conversion for answer/registry files

The runtime tools 7z, genisoimage, isoinfo, wget, curl, jq, and nc come from the base image.

9.2 The assets/ directory

assets/*.xml holds per-version unattended answer files (win11x64.xml, win10x64-enterprise-eval.xml, …) — 40 templates spanning Vista through Server 2025. updateXML() patches these at runtime; detectImage()/setXML() select the right one.


10. Code Quality & Conventions

  • Strict Bash everywhere: every script begins set -Eeuo pipefail. Failures propagate, and the install pipeline maps each to an exit code.
  • Defensive defaults: variables use : "${VAR:=""}" parameter expansion so the scripts run cleanly under nounset.
  • Procedural source modules: scripts are sourced (not exec'd) and run top-to-bottom at source time — order in entry.sh is significant.
  • Table-driven logic: version IDs, languages, editions, and the four mirror sets are all case tables in define.sh, making them easy to audit and extend.
  • Heavy reliance on external CLIs: the pipeline orchestrates wimlib-imagex, genisoimage, 7z, xmllint, sed, curl/wget. Behavior is sensitive to those tools' versions (pinned via the base image).

11. Potential Improvements

These are observations for maintainers and forkers, derived from the source and community issues. They are not defects in normal operation.

  • [ ] NET_RAW discoverability — RDP failing without NET_RAW on some hosts (community issue) is not surfaced in the default error path. A startup probe that detects the missing capability and prints a hint would cut support load.
  • [ ] passt backend stability — a reported passt.avx2 SIGABRT causes guest network loss on certain CPUs. Auto-falling back to bridge networking on a passt crash would harden the default path.
  • [ ] SMB1 default — server min protocol = NT1 is enabled unconditionally for legacy guests. Gating SMB1 behind a flag (or per-version) would reduce the attack surface for modern-only deployments.
  • [ ] Privileged Kubernetes manifest — shipping a least-privilege variant (device plugin + dropped privileged: true) alongside the current manifest would suit hardened clusters.
  • [ ] Download-path observability — the Microsoft handshake has many failure points; a DEBUG-independent structured log of which acquisition method (Mido/eval/ESD/mirror N) ultimately succeeded would aid air-gapped planning.

12. Quick Reference

# Start / stop
docker compose up -d
docker compose down                 # triggers graceful ACPI shutdown

# Logs & status
docker compose logs -f              # watch install pipeline
docker exec windows cat /etc/version

# Common overrides (compose environment:)
VERSION: "10"        # edition or ISO URL
RAM_SIZE: "8G"
CPU_CORES: "4"
DISK_SIZE: "256G"
USERNAME: "admin"; PASSWORD: "<strong>"
MANUAL: "Y"          # manual Setup
DHCP: "Y"            # own LAN IP (needs /dev/vhost-net)
VERIFY: "Y"          # checksum the download

# Required runtime
--device=/dev/kvm --device=/dev/net/tun --cap-add NET_ADMIN
-p 8006:8006 -p 3389:3389/tcp -p 3389:3389/udp
-v ./windows:/storage
Connect Address Notes
Web viewer http://127.0.0.1:8006/ Install/quick view; low quality, no audio/clipboard
RDP container IP :3389 Full quality; Docker/admin by default