From superpowers-lab
Create, start, stop, restart, or SSH into a headless Windows 11 VM running in Docker with KVM acceleration. Use for Windows tasks on Linux hosts without GUI.
npx claudepluginhub obra/superpowers-marketplace --plugin superpowers-labThis skill is limited to using the following tools:
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.
Deploy a virtual machine in a Kube-DC project with SSH access, cloud-init configuration, and optional external IP exposure. Supports Ubuntu, Debian, CentOS, Fedora, Alpine, openSUSE, Gentoo, and Windows images.
Sets up WSL2 dev environment on Windows with shell config, Git, SSH keys, Node.js, Python, and cross-platform paths. Use for new Windows dev machines, first WSL2 configs, or adding tools.
Provides step-by-step setup guides for developer environments on Windows, macOS, Linux, and WSL. Covers package managers, shells, VS Code, Docker, AI tools, databases, and security for new machine setups or team onboarding.
Share bugs, ideas, or general feedback.
Manage a headless Windows 11 VM running via dockur/windows in Docker with KVM acceleration. The VM is accessible via SSH only — no RDP or GUI required.
/dev/kvm must exist — check with ls /dev/kvm)sshpass (sudo apt install sshpass)imagemagick (optional, for screenshot debugging: sudo apt install imagemagick)windows11$HOME/windows-vm/
storage/ — VM disk image (managed by dockur, wiped on recreate)iso/win11x64.iso — cached Windows ISO (7.3GB, persists across recreates)oem/install.bat — post-install script (installs OpenSSH Server)localhost:2222 (bound to 127.0.0.1 only)localhost:3389 (bound to 127.0.0.1 only, fallback)localhost:8006 (VNC in browser, for debugging)Ensure directories exist:
mkdir -p "$HOME/windows-vm/oem" "$HOME/windows-vm/storage" "$HOME/windows-vm/iso"
Ensure $HOME/windows-vm/oem/install.bat exists with OpenSSH setup:
@echo off
echo Installing OpenSSH Server...
powershell -Command "Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0" 2>nul
powershell -Command "Get-WindowsCapability -Online -Name OpenSSH.Server* | Add-WindowsCapability -Online" 2>nul
dism /Online /Add-Capability /CapabilityName:OpenSSH.Server~~~~0.0.1.0 2>nul
powershell -Command "Start-Service sshd" 2>nul
powershell -Command "Set-Service -Name sshd -StartupType Automatic"
powershell -Command "New-ItemProperty -Path 'HKLM:\SOFTWARE\OpenSSH' -Name DefaultShell -Value 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe' -PropertyType String -Force"
powershell -Command "New-NetFirewallRule -Name 'OpenSSH-Server' -DisplayName 'OpenSSH Server' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22"
powershell -Command "Get-Service sshd" 2>nul
echo Done.
If recreating, remove the old container and disk:
docker stop windows11 && docker rm windows11
rm -f "$HOME/windows-vm/storage/data.img"
Launch the container. There are two cases:
If cached ISO exists ($HOME/windows-vm/iso/win11x64.iso):
docker run -d \
--name windows11 \
-p 127.0.0.1:3389:3389 \
-p 127.0.0.1:2222:22 \
-p 127.0.0.1:8006:8006 \
-e RAM_SIZE="8G" \
-e CPU_CORES="4" \
-e DISK_SIZE="64G" \
-e USERNAME="user" \
-e PASSWORD="password" \
--cap-add NET_ADMIN \
--device /dev/kvm \
-v "$HOME/windows-vm/storage:/storage" \
-v "$HOME/windows-vm/oem:/oem" \
-v "$HOME/windows-vm/iso/win11x64.iso:/boot.iso" \
dockurr/windows
First time (no cached ISO) — omit the /boot.iso mount and add VERSION:
docker run -d \
--name windows11 \
-p 127.0.0.1:3389:3389 \
-p 127.0.0.1:2222:22 \
-p 127.0.0.1:8006:8006 \
-e RAM_SIZE="8G" \
-e CPU_CORES="4" \
-e DISK_SIZE="64G" \
-e VERSION="win11" \
-e USERNAME="user" \
-e PASSWORD="password" \
--cap-add NET_ADMIN \
--device /dev/kvm \
-v "$HOME/windows-vm/storage:/storage" \
-v "$HOME/windows-vm/oem:/oem" \
dockurr/windows
After the ISO downloads and Windows boots, immediately copy the ISO out before
the container is ever stopped (dockur wipes /storage on recreate):
cp "$HOME/windows-vm/storage/win11x64.iso" "$HOME/windows-vm/iso/win11x64.iso"
Wait for Windows install + OpenSSH setup to complete. This takes 20-30 minutes for a fresh install (the OEM install.bat runs at the end of Windows OOBE and downloads OpenSSH from Microsoft, which is slow). Monitor with:
docker logs -f windows11
You can also watch the VM screen via the web console at http://localhost:8006.
To check if SSH is up:
sshpass -p 'password' ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 -p 2222 user@localhost "whoami"
Once SSH is responding, install Node.js and Claude Code by piping a setup script via stdin (avoids PowerShell escaping hell over SSH):
cat << 'PS' | sshpass -p 'password' ssh -o StrictHostKeyChecking=no -p 2222 user@localhost "powershell -ExecutionPolicy Bypass -Command -"
# Download and install Node.js silently
Invoke-WebRequest -Uri 'https://nodejs.org/dist/v22.14.0/node-v22.14.0-x64.msi' -OutFile 'C:\Users\user\node-install.msi'
Start-Process msiexec.exe -ArgumentList '/i C:\Users\user\node-install.msi /qn /norestart' -Wait -Verb RunAs
Write-Host "Node.js installed"
# Install Claude Code globally
& 'C:\Program Files\nodejs\npm.cmd' install -g @anthropic-ai/claude-code
Write-Host "Claude Code installed"
# Add npm global bin to SYSTEM PATH (user PATH is not read by sshd)
$systemPath = [Environment]::GetEnvironmentVariable('Path', 'Machine')
$additions = @()
if ($systemPath -notlike '*AppData*npm*') { $additions += 'C:\Users\user\AppData\Roaming\npm' }
if ($systemPath -notlike '*Git\cmd*') { $additions += 'C:\Program Files\Git\cmd' }
if ($additions.Count -gt 0) {
[Environment]::SetEnvironmentVariable('Path', $systemPath + ';' + ($additions -join ';'), 'Machine')
Write-Host "Added to system PATH: $($additions -join ', ')"
}
# Set execution policy machine-wide (required for claude.ps1)
Set-ExecutionPolicy RemoteSigned -Scope LocalMachine -Force -ErrorAction SilentlyContinue
# Create system-wide PowerShell profile that rebuilds PATH from registry on login.
# Without this, interactive SSH sessions don't pick up the full system PATH.
$profileDir = Split-Path $PROFILE.AllUsersAllHosts
if (-not (Test-Path $profileDir)) { New-Item -ItemType Directory -Path $profileDir -Force }
@'
$machinePath = [Environment]::GetEnvironmentVariable('Path', 'Machine')
$userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
$env:Path = "$machinePath;$userPath"
'@ | Set-Content -Path $PROFILE.AllUsersAllHosts -Force
Write-Host "PowerShell profile created"
# Restart sshd so it picks up the new PATH
Restart-Service sshd -Force
PS
Note: the connection will drop when sshd restarts — that's expected.
Clear the stale host key (new VM = new host key) and verify:
ssh-keygen -f ~/.ssh/known_hosts -R '[localhost]:2222'
sshpass -p 'password' ssh -o StrictHostKeyChecking=no -p 2222 user@localhost "claude --version"
docker start windows11
docker stop windows11
docker restart windows11
docker ps -f name=windows11 --format "table {{.Status}}\t{{.Ports}}"
docker logs windows11 2>&1 | tail -5
ssh -p 2222 user@localhost
docker exec windows11 bash -c "echo 'screendump /tmp/screen.ppm' | nc -w 2 localhost 7100" > /dev/null 2>&1
sleep 1
docker cp windows11:/tmp/screen.ppm /tmp/screen.ppm
convert /tmp/screen.ppm /tmp/screen.png
/storage volume is managed by dockur and gets wiped on recreate. Store the ISO separately in $HOME/windows-vm/iso/ and mount it as /boot.iso to skip the 7.3GB download.--cap-add NET_ADMIN is required for port forwarding to work. Without it, QEMU falls back to user-mode networking and port forwarding silently fails.--device /dev/kvm is required for hardware acceleration.data.img are fast (~2 min).127.0.0.1 only — not exposed to the network.-e VERSION="win11" when mounting /boot.iso — the version is auto-detected from the ISO.irm https://claude.ai/install.ps1 | iex) will report success but claude won't work without Node. Install Node.js via MSI first.C:\Program Files\nodejs to PATH but not C:\Users\user\AppData\Roaming\npm (where npm install -g puts binaries). Must add it to the system PATH (not user PATH) because OpenSSH's sshd only reads system PATH. After changing system PATH, restart sshd.Restricted, which blocks claude.ps1. Must set to RemoteSigned at LocalMachine scope (not CurrentUser) for it to take effect in SSH sessions.powershell -ExecutionPolicy Bypass -Command - instead.$PROFILE.AllUsersAllHosts) that rebuilds $env:Path from the registry on every login.ssh-keygen -R '[localhost]:2222' to clear the old one.