Catalog
obra/windows-vm

obra

windows-vm

Create, 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.

global
Allowed Tools
BashReadWrite
0installs0uses~2.5k
v1.0Saved May 2, 2026

Headless Windows 11 VM

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.

Host prerequisites

  • Docker
  • KVM support (/dev/kvm must exist — check with ls /dev/kvm)
  • sshpass (sudo apt install sshpass)
  • imagemagick (optional, for screenshot debugging: sudo apt install imagemagick)

Configuration

  • Container name: windows11
  • VM directory: $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)
  • Credentials: user / password
  • SSH: localhost:2222 (bound to 127.0.0.1 only)
  • RDP: localhost:3389 (bound to 127.0.0.1 only, fallback)
  • Web console: localhost:8006 (VNC in browser, for debugging)
  • Resources: 8GB RAM, 4 CPU cores, 64GB disk

Actions

create — First-time setup or full recreate

  1. Ensure directories exist:

    mkdir -p "$HOME/windows-vm/oem" "$HOME/windows-vm/storage" "$HOME/windows-vm/iso"
    
  2. 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.
    
  3. If recreating, remove the old container and disk:

    docker stop windows11 && docker rm windows11
    rm -f "$HOME/windows-vm/storage/data.img"
    
  4. 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"
    
  5. 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"
    
  6. 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.

  7. 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"
    

start — Start a stopped VM

docker start windows11

stop — Stop the VM

docker stop windows11

restart — Restart the VM

docker restart windows11

status — Check VM status

docker ps -f name=windows11 --format "table {{.Status}}\t{{.Ports}}"
docker logs windows11 2>&1 | tail -5

ssh — Connect to the VM

ssh -p 2222 user@localhost

screenshot — See what's on the VM screen (for debugging)

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

Important Notes

  • ISO caching: The /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.
  • Boot time: Fresh install takes 20-30 min (Windows install + OpenSSH download from Microsoft). Subsequent boots from existing data.img are fast (~2 min).
  • Ports are bound to 127.0.0.1 only — not exposed to the network.
  • Do NOT use -e VERSION="win11" when mounting /boot.iso — the version is auto-detected from the ISO.

Post-install gotchas

  • Node.js is not pre-installed — the Claude Code install script (irm https://claude.ai/install.ps1 | iex) will report success but claude won't work without Node. Install Node.js via MSI first.
  • npm global bin not in PATH — Node's MSI adds 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.
  • PowerShell execution policy — Default policy is Restricted, which blocks claude.ps1. Must set to RemoteSigned at LocalMachine scope (not CurrentUser) for it to take effect in SSH sessions.
  • Escaping hell — Running PowerShell commands over SSH with nested quotes is unreliable. Pipe scripts via stdin using powershell -ExecutionPolicy Bypass -Command - instead.
  • Interactive SSH sessions don't get full PATH — Windows OpenSSH sshd doesn't properly propagate the system PATH to interactive PowerShell sessions. Fix: create a system-wide PowerShell profile ($PROFILE.AllUsersAllHosts) that rebuilds $env:Path from the registry on every login.
  • winget may not work — The Microsoft Store certificate can fail in a VM. Use direct MSI/installer downloads instead.
  • Host key changes — Each recreated VM gets new SSH host keys. Run ssh-keygen -R '[localhost]:2222' to clear the old one.
Files1
1 files · 1.0 KB

Select a file to preview

Grade adjusted by static analysis guardrails

AI scored this skill as grade B, but static analysis findings capped it to C:

  • Hardcoded credentials or secrets detected in content (max: C)
  • SSH key or credentials file access (max: B)

Overall Score

71/100

Grade

C

Adequate

Safety

62

Quality

75

Clarity

78

Completeness

68

Summary

This skill manages a headless Windows 11 VM running in Docker with SSH access via the dockur/windows image. It provides commands to create, start, stop, restart, and SSH into the VM, along with Node.js and Claude Code installation. The skill is designed for developers who need a complete Windows environment with SSH-based remote access.

Static Analysis Findings

3 findings

Patterns detected by deterministic static analysis before AI scoring. Hover over any finding code for detailed information and remediation guidance.

Credential Exposure
SEC-022SSH/Credentials File AccessMax: B

SSH key or credentials file access

SKILL.md~/.ssh/
SEC-023Plaintext Password or Secret2x in 1 fileMax: C

Password or secret in plaintext

SKILL.mdPASSWORD="password2x
Destructive Operation
SEC-002Privilege Escalation2x in 1 file

Privilege escalation (sudo)

SKILL.mdsudo a2x

Detected Capabilities

Docker container lifecycle management (create, start, stop, restart)SSH access configuration and connection to Windows VMFile system management (directory creation, ISO caching, mounting volumes)PowerShell script execution over SSHNode.js and npm package installationWindows service management (OpenSSH Server setup)Network port binding and firewall configurationKVM hardware acceleration device accessSystem environment variable and PATH manipulation

Trigger Keywords

Phrases that MCP clients use to match this skill to user intent.

windows vm setupheadless windowsssh windows accessdocker windows containernode.js on windows

Risk Signals

WARNING

sudo apt install (privilege escalation)

SKILL.md | Host prerequisites section
CRITICAL

Plaintext password in environment variable (PASSWORD="password")

SKILL.md | create action, Docker run commands
INFO

SSH key and known_hosts file access (~/.ssh/known_hosts)

SKILL.md | step 7 of create action
WARNING

System PATH manipulation and environment variable modification

SKILL.md | step 6 setup script
INFO

PowerShell execution policy change to RemoteSigned

SKILL.md | step 6 setup script
WARNING

sshpass with hardcoded password in multiple commands

SKILL.md | steps 5-7 of create action
INFO

Network request for Node.js MSI download

SKILL.md | step 6, Invoke-WebRequest to nodejs.org
INFO

Cap-add NET_ADMIN and /dev/kvm device access

SKILL.md | Docker run commands

Referenced Domains

External domains referenced in skill content, detected by static analysis.

claude.aigithub.comlocalhostnodejs.org

Use Cases

  • Set up a Windows development environment for testing Windows-specific code
  • Run Windows applications or tools in a containerized, headless VM
  • Test .NET or PowerShell scripts in an isolated Windows environment
  • Debug Windows-specific issues without leaving a Linux/macOS host
  • Run Claude Code within a Windows SSH session for remote pair programming

Quality Notes

  • POSITIVE: Comprehensive documentation of prerequisites, configuration, and each action (create, start, stop, restart, status, ssh, screenshot). Clear step-by-step instructions.
  • POSITIVE: Excellent 'Important Notes' and 'Post-install gotchas' sections that document non-obvious Windows/SSH behavior and save users debugging time.
  • POSITIVE: Realistic timing expectations (20-30 min for fresh install, 2 min for subsequent boots) and monitoring commands provided.
  • POSITIVE: Environment variable and port binding clearly documented with explanations of why each setting is needed.
  • NEGATIVE: No guidance on changing the default hardcoded password. Skill should include a step to regenerate credentials or document how users should modify them for their own instances.
  • NEGATIVE: Error handling is minimal. Skill does not describe what to do if Docker installation fails, KVM is unavailable, or SSH setup times out. No retry logic or fallback instructions provided.
  • NEGATIVE: The `screenshot` action mentions ImageMagick as optional but doesn't explain how to install it or handle the case where it's missing. The command will silently fail if convert is not available.
  • NEGATIVE: No mention of resource constraints. The 8GB RAM and 4 CPU allocation may not be available on all systems. The skill should recommend checking `docker stats` or suggest downsizing for resource-constrained hosts.
  • NEGATIVE: The post-install setup script is very long and complex. Breaking it into smaller, separately documented steps or providing it as a separate file would improve readability.
  • POSITIVE: The skill clearly documents that ports are bound to 127.0.0.1 only, mitigating some network security risks.
  • NEGATIVE: No cleanup or teardown instructions. Users should be told how to completely remove the VM and free up disk space (7.3GB ISO + 64GB disk).
Model: claude-haiku-4-5-20251001Analyzed: May 2, 2026

Reviews

Add this skill to your library to leave a review.

No reviews yet

Be the first to share your experience.

Add obra/windows-vm to your library

Command Palette

Search for a command to run...