$npx -y skills add obra/superpowers-lab --skill windows-vmCreate, manage, or connect to a headless Windows 11 VM running in Docker with SSH access. Use when the user wants to spin up, stop, restart, or SSH into a Windows VM.
| 1 | # Headless Windows 11 VM |
| 2 | |
| 3 | Manage a headless Windows 11 VM running via [dockur/windows](https://github.com/dockur/windows) in Docker with KVM acceleration. The VM is accessible via SSH only — no RDP or GUI required. |
| 4 | |
| 5 | ## Host prerequisites |
| 6 | |
| 7 | - Docker |
| 8 | - KVM support (`/dev/kvm` must exist — check with `ls /dev/kvm`) |
| 9 | - `sshpass` (`sudo apt install sshpass`) |
| 10 | - `imagemagick` (optional, for screenshot debugging: `sudo apt install imagemagick`) |
| 11 | |
| 12 | ## Configuration |
| 13 | |
| 14 | - **Container name**: `windows11` |
| 15 | - **VM directory**: `$HOME/windows-vm/` |
| 16 | - `storage/` — VM disk image (managed by dockur, wiped on recreate) |
| 17 | - `iso/win11x64.iso` — cached Windows ISO (7.3GB, persists across recreates) |
| 18 | - `oem/install.bat` — post-install script (installs OpenSSH Server) |
| 19 | - **Credentials**: user / password |
| 20 | - **SSH**: `localhost:2222` (bound to 127.0.0.1 only) |
| 21 | - **RDP**: `localhost:3389` (bound to 127.0.0.1 only, fallback) |
| 22 | - **Web console**: `localhost:8006` (VNC in browser, for debugging) |
| 23 | - **Resources**: 8GB RAM, 4 CPU cores, 64GB disk |
| 24 | |
| 25 | ## Actions |
| 26 | |
| 27 | ### create — First-time setup or full recreate |
| 28 | |
| 29 | 1. Ensure directories exist: |
| 30 | ```bash |
| 31 | mkdir -p "$HOME/windows-vm/oem" "$HOME/windows-vm/storage" "$HOME/windows-vm/iso" |
| 32 | ``` |
| 33 | |
| 34 | 2. Ensure `$HOME/windows-vm/oem/install.bat` exists with OpenSSH setup: |
| 35 | ```bat |
| 36 | @echo off |
| 37 | echo Installing OpenSSH Server... |
| 38 | powershell -Command "Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0" 2>nul |
| 39 | powershell -Command "Get-WindowsCapability -Online -Name OpenSSH.Server* | Add-WindowsCapability -Online" 2>nul |
| 40 | dism /Online /Add-Capability /CapabilityName:OpenSSH.Server~~~~0.0.1.0 2>nul |
| 41 | powershell -Command "Start-Service sshd" 2>nul |
| 42 | powershell -Command "Set-Service -Name sshd -StartupType Automatic" |
| 43 | powershell -Command "New-ItemProperty -Path 'HKLM:\SOFTWARE\OpenSSH' -Name DefaultShell -Value 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe' -PropertyType String -Force" |
| 44 | powershell -Command "New-NetFirewallRule -Name 'OpenSSH-Server' -DisplayName 'OpenSSH Server' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22" |
| 45 | powershell -Command "Get-Service sshd" 2>nul |
| 46 | echo Done. |
| 47 | ``` |
| 48 | |
| 49 | 3. If recreating, remove the old container and disk: |
| 50 | ```bash |
| 51 | docker stop windows11 && docker rm windows11 |
| 52 | rm -f "$HOME/windows-vm/storage/data.img" |
| 53 | ``` |
| 54 | |
| 55 | 4. Launch the container. There are two cases: |
| 56 | |
| 57 | **If cached ISO exists** (`$HOME/windows-vm/iso/win11x64.iso`): |
| 58 | ```bash |
| 59 | docker run -d \ |
| 60 | --name windows11 \ |
| 61 | -p 127.0.0.1:3389:3389 \ |
| 62 | -p 127.0.0.1:2222:22 \ |
| 63 | -p 127.0.0.1:8006:8006 \ |
| 64 | -e RAM_SIZE="8G" \ |
| 65 | -e CPU_CORES="4" \ |
| 66 | -e DISK_SIZE="64G" \ |
| 67 | -e USERNAME="user" \ |
| 68 | -e PASSWORD="password" \ |
| 69 | --cap-add NET_ADMIN \ |
| 70 | --device /dev/kvm \ |
| 71 | -v "$HOME/windows-vm/storage:/storage" \ |
| 72 | -v "$HOME/windows-vm/oem:/oem" \ |
| 73 | -v "$HOME/windows-vm/iso/win11x64.iso:/boot.iso" \ |
| 74 | dockurr/windows |
| 75 | ``` |
| 76 | |
| 77 | **First time (no cached ISO)** — omit the `/boot.iso` mount and add `VERSION`: |
| 78 | ```bash |
| 79 | docker run -d \ |
| 80 | --name windows11 \ |
| 81 | -p 127.0.0.1:3389:3389 \ |
| 82 | -p 127.0.0.1:2222:22 \ |
| 83 | -p 127.0.0.1:8006:8006 \ |
| 84 | -e RAM_SIZE="8G" \ |
| 85 | -e CPU_CORES="4" \ |
| 86 | -e DISK_SIZE="64G" \ |
| 87 | -e VERSION="win11" \ |
| 88 | -e USERNAME="user" \ |
| 89 | -e PASSWORD="password" \ |
| 90 | --cap-add NET_ADMIN \ |
| 91 | --device /dev/kvm \ |
| 92 | -v "$HOME/windows-vm/storage:/storage" \ |
| 93 | -v "$HOME/windows-vm/oem:/oem" \ |
| 94 | dockurr/windows |
| 95 | ``` |
| 96 | After the ISO downloads and Windows boots, **immediately** copy the ISO out before |
| 97 | the container is ever stopped (dockur wipes `/storage` on recreate): |
| 98 | ```bash |
| 99 | cp "$HOME/windows-vm/storage/win11x64.iso" "$HOME/windows-vm/iso/win11x64.iso" |
| 100 | ``` |
| 101 | |
| 102 | 5. Wait for Windows install + OpenSSH setup to complete. This takes **20-30 minutes** for a |
| 103 | fresh install (the OEM install.bat runs at the end of Windows OOBE and downloads OpenSSH |
| 104 | from Microsoft, which is slow). Monitor with: |
| 105 | ```bash |
| 106 | docker logs -f windows11 |
| 107 | ``` |
| 108 | You can also watch the VM screen via the web console at `http://localhost:8006`. |
| 109 | |
| 110 | To check if SSH is up: |
| 111 | ```bash |
| 112 | sshpass -p 'password' ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 -p 2222 user@localhost "whoami" |
| 113 | ``` |
| 114 | |
| 115 | 6. Once SSH is responding, install Node.js and Claude Code by piping a setup script via stdin |
| 116 | (avoids PowerShell escaping hell over SSH): |
| 117 | ```bash |
| 118 | cat << 'PS' | sshpass -p 'password' ssh -o StrictHostKeyChecking=no -p 2222 user@localhost "powershell -ExecutionPolicy Bypass -Command -" |
| 119 | # Download and install Node.js silently |
| 120 | Invoke-WebRequest -Uri 'https://nodejs.org/dist/v22.14.0/node-v22.14.0-x64.msi' -OutFi |