Last active
October 14, 2025 18:22
-
-
Save rrmistry/04d282751122fd41fc5ebb9962e60602 to your computer and use it in GitHub Desktop.
Run Ubuntu VM using Qemu in Windows User Space (without admin rights)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| $ErrorActionPreference = "Stop" | |
| If (-not (Get-Command -Name "scoop" -ErrorAction "SilentlyContinue")) { | |
| # Install Scoop | |
| Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser -Force | |
| Invoke-RestMethod get.scoop.sh | Invoke-Expression | |
| } | |
| If (-not (Get-Command -Name "qemu-system-x86_64" -ErrorAction "SilentlyContinue")) { | |
| # Install qemu | |
| scoop install qemu | |
| } | |
| $qemuFolder = "$ENV:USERPROFILE/qemu/" | |
| If (-Not (Test-Path -Path $qemuFolder -ErrorAction "SilentlyContinue")) { | |
| New-Item -Path $qemuFolder -ItemType "Directory" -Force -Verbose | |
| } | |
| # Define paths and settings | |
| $qemuPath = "qemu-system-x86_64.exe" # Adjust this path to where your QEMU executable is located | |
| $qemuImgPath = "qemu-img.exe" # Adjust this path to where your QEMU image utility is located | |
| $diskImagePath = "$qemuFolder/linux.qcow2" # Path where the VM's disk image will be created | |
| $memorySize = "4096M" # Memory size (e.g., 2048M for 2GB) | |
| $cpuCount = 8 # Number of CPUs | |
| $isNewBootDisk = $false | |
| If (-not (Test-Path -Path $diskImagePath -ErrorAction SilentlyContinue)) { | |
| $qemuImageUrl = "https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-amd64.qcow2" | |
| # Download the QEMU Image file | |
| Invoke-WebRequest -Uri $qemuImageUrl -OutFile $diskImagePath -Verbose | |
| Write-Host "Download completed! The QEMU Image is saved at: $diskImagePath" -ForegroundColor "Green" | |
| # Resize to add working space (e.g., add 20GB) | |
| & $qemuImgPath resize $diskImagePath +100G | |
| $isNewBootDisk = $true | |
| } | |
| # Run QEMU with the specified settings and setup Ubuntu automatically | |
| $argumentList = @( | |
| "-m", $memorySize, | |
| "-smp", $cpuCount, | |
| "-drive", "file=$diskImagePath,format=qcow2,if=virtio", | |
| "-boot", "d", | |
| "-netdev", "user,id=mynet0,hostfwd=tcp::8022-:22,hostfwd=tcp::4180-:4180,hostfwd=tcp::3000-:3000,hostfwd=tcp::9229-:9229,hostfwd=tcp::8000-:8000", | |
| "-device", "virtio-net,netdev=mynet0", | |
| "-accel", "whpx", # Hardware acceleration on Windows using Hypbervisor | |
| "-no-reboot", | |
| "-nographic" | |
| ) | |
| function Test-QEMUProcessExists { | |
| param( | |
| [string]$QemuPath, | |
| [string[]]$ArgumentList | |
| ) | |
| $qemuExeName = [System.IO.Path]::GetFileNameWithoutExtension($QemuPath) | |
| $existingProcesses = Get-Process -Name $qemuExeName -ErrorAction SilentlyContinue | |
| # Convert argument list to normalized string for comparison | |
| $newArgsString = ($ArgumentList -join ' ') -replace '\s+', ' ' | |
| foreach ($proc in $existingProcesses) { | |
| try { | |
| $wmiProcess = Get-CimInstance Win32_Process -Filter "ProcessId = $($proc.Id)" | |
| $commandLine = $wmiProcess.CommandLine | |
| # Extract just the arguments part (everything after the executable) | |
| $executablePath = $wmiProcess.ExecutablePath | |
| if ($commandLine -match [regex]::Escape($executablePath)) { | |
| $existingArgs = $commandLine.Substring($commandLine.IndexOf($executablePath) + $executablePath.Length).Trim().Trim('"').Trim() | |
| } else { | |
| continue | |
| } | |
| # Normalize whitespace for comparison | |
| $normalizedExistingArgs = $existingArgs -replace '\s+', ' ' | |
| $normalizedNewArgs = $newArgsString -replace '\s+', ' ' | |
| if ($normalizedExistingArgs -eq $normalizedNewArgs) { | |
| return @{ | |
| Found = $true | |
| ProcessId = $proc.Id | |
| CommandLine = $commandLine | |
| } | |
| } | |
| } | |
| catch { | |
| continue | |
| } | |
| } | |
| return @{ Found = $false } | |
| } | |
| # # Run QEMU | |
| # Start-Process -FilePath $qemuPath -ArgumentList $argumentList -NoNewWindow -Wait -PassThru | |
| # Check for existing process | |
| $result = Test-QEMUProcessExists -QemuPath $qemuPath -ArgumentList $argumentList | |
| if ($result.Found) { | |
| Write-Host "Existing QEMU process found!" -ForegroundColor Green | |
| Write-Host "PID: $($result.ProcessId)" -ForegroundColor Yellow | |
| Write-Host "Command: $($result.CommandLine)" -ForegroundColor Cyan | |
| } else { | |
| Write-Host "No matching process found. Launching QEMU..." -ForegroundColor Yellow | |
| $newProcess = $(Start-Process -FilePath $qemuPath -ArgumentList $argumentList -WindowStyle Hidden -Verbose) | |
| Write-Host "Started new process with PID: $($newProcess)" -ForegroundColor Green | |
| $result = Test-QEMUProcessExists -QemuPath $qemuPath -ArgumentList $argumentList | |
| if ($result.Found) { | |
| Write-Host "Existing QEMU process found!" -ForegroundColor Green | |
| Write-Host "PID: $($result.ProcessId)" -ForegroundColor Yellow | |
| Write-Host "Command: $($result.CommandLine)" -ForegroundColor Cyan | |
| } else { | |
| Write-Host "Warning! QEMU is not running!" -ForegroundColor Yellow | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Install VS Code CLI | |
| apk add gcompat libstdc++ curl | |
| curl -sL "https://code.visualstudio.com/sha/download?build=stable&os=cli-alpine-x64" --output /tmp/vscode-cli.tar.gz \ | |
| && tar -xf /tmp/vscode-cli.tar.gz -C /usr/bin \ | |
| && rm /tmp/vscode-cli.tar.gz | |
| # Setup script for quick access to code server | |
| echo "code serve-web --host 0.0.0.0 --port 8000 --without-connection-token --accept-server-license-terms" > ~/code-server.sh | |
| chmod u+x ~/code-server.sh | |
| # Edit sshd_config | |
| vi /etc/ssh/sshd_config | |
| # # Change this line | |
| # PermitRootLogin yes | |
| # AllowTcpForwarding yes | |
| # Restart sshd service | |
| rc-service sshd restart | |
| # Install docker | |
| apk add docker | |
| apk add docker-cli-compose | |
| # Run docker daemon in the background | |
| addgroup ${USER} docker | |
| rc-update add docker default | |
| service docker start | |
| rc-update add cgroups | |
| # Install Python | |
| apk add python3 py3-pip |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment