A wsl --install -d Ubuntu-22.04 on a Windows Server 2022 guest with 8 GB RAM leaves vmmem holding 4.1 GB within twenty minutes, because WSL2 runs a real Linux kernel inside a utility VM and the VM’s memory is not returned to the host on an aggressive schedule. On a dedicated workstation that is invisible. On a VPS that also runs IIS, it is the difference between a healthy server and one that starts swapping at 3 p.m.
Before sizing a host that runs both, you need to know what the WSL2 subsystem actually reserves and how to cap it, because the defaults assume a desktop with tens of gigabytes and no other workloads.
What vmmem Actually Holds
The WSL2 utility VM boots a Microsoft-built kernel and a minimal userland. At idle with one distribution installed and no processes running, expect vmmem to report 800 MB to 1.2 GB in Task Manager. That figure is not the amount of memory your Linux processes are using — it is what the utility VM has been granted and has touched, including page cache. Launch a build that touches 20,000 files and the cache grows; Linux deliberately fills free memory with cache, and the utility VM does not hand it back promptly.
From inside the distribution, free -m tells you the truth about what Linux thinks it has. From the Windows side, the number that matters is the working set of vmmem and, more importantly, the total commit charge: Get-Counter '\Memory\Committed Bytes','\Memory\Available MBytes'. If Available MBytes stays below 15% of installed RAM with no SQL or IIS load, the host is over-subscribed before you have added a workload.
Dynamic Memory and Why the Balloon Misbehaves
Hyper-V dynamic memory and WSL2’s own memory reclaim are two separate systems that do not negotiate. WSL2 defines its own ceiling in %UserProfile%\.wslconfig:
[wsl2]
memory=4GB
processors=2
swap=2GB
swapFile=D:\\wsl\\swap.vhdx
localhostForwarding=true
Without a memory= line, WSL2 on Windows Server defaults to 50% of total RAM, or 8 GB, whichever is lower on older builds. On a 16 GB VPS that means an 8 GB ceiling — half your memory allocated to a subsystem that is probably running a CLI tool. Set memory= explicitly and set processors= explicitly, because the default lets Linux see every logical processor on the host, which makes its scheduler and make -j behave as though the machine were idle.
Inside a Hyper-V VPS with dynamic memory enabled on the host, the balloon driver can reclaim memory from the Windows guest while vmmem inside it believes it owns its allocation. The result is guest-level pressure that Windows resolves by trimming working sets, which pushes WSL2’s own pages to the guest page file and turns a 2-second command into a 40-second one. Check for it with Get-Counter '\Memory\Pages Input/sec' -SampleInterval 2; sustained values above 100 during light WSL use indicate balloon pressure, not a Linux problem.
Nested Virtualization: The Hard Requirement
WSL2 is a hypervisor, so it needs hardware virtualization exposed to the guest. On a KVM-based VPS this requires the host to pass through vmx or svm, which most providers disable by default because it enables guest-escapes-hypervisor attacks. Verify before you buy:
Get-CimInstance Win32_Processor | Select VirtualizationFirmwareEnabled,VMMonitorModeExtensions
systeminfo | Select-String "Hyper-V Requirements" -Context 0,4
VMMonitorModeExtensions must read True. If it reads False or the systeminfo block shows “A hypervisor has been detected”, WSL2 will fail at boot with WslRegisterDistribution failed with error: 0x80370102 and you need the provider to enable nested virtualization on the host — it cannot be fixed from inside the guest. Confirm the feature is offered on the Windows VPS configuration guide before ordering, because it is not universal.
Disk Cost Is Larger Than the Image Size
The ext4 filesystem lives inside ext4.vhdx, which grows but does not shrink automatically. A 2 GB distribution becomes a 30 GB file after a few months of node_modules and container layers, and deleting files inside Linux does not return space to Windows. Two operations are needed: trim from inside, then compact from outside.
Inside the distribution, confirm the discard option is active with mount | grep ext4 — the utility VM mounts with discard by default on recent builds. Then on Windows, after wsl --shutdown, run Optimize-VHD -Path D:\wsl\ext4.vhdx -Mode Full (requires the Hyper-V PowerShell module) or use diskpart with compact vdisk. Budget 25-40 GB of data volume for a working WSL2 install and set swapFile= onto that same volume rather than the system disk.
Sizing a Host That Also Runs IIS or SQL Server
Add the numbers up before choosing a plan. IIS with a typical ASP.NET Core application needs 1-1.5 GB for two worker processes plus 0.5 GB of headroom for the OS. SQL Server needs its buffer pool plus a minimum of 1 GB overhead, and Microsoft’s guidance is to leave 4 GB to the OS on a dedicated database server. WSL2 with a capped 4 GB and a 2 GB swap file adds 4 GB of commit charge and up to 6 GB of disk.
| Component | RAM floor | Notes |
|---|---|---|
| Windows Server 2022 base | 1.5 GB | Higher after patch installation |
| IIS + ASP.NET Core | 1.5 GB | Two app pool workers under load |
| SQL Server buffer pool | 4 GB | Plus 1 GB overhead |
| WSL2 capped | 4 GB | Set memory=4GB in .wslconfig |
| Headroom for balloon | 2 GB | Never plan to use the last 10% |
That table lands at 14 GB before headroom, which means a 16 GB VPS is the practical minimum and 24 GB is comfortable when SQL Server handles real traffic. Two vCPUs will be contended during dotnet build inside WSL while IIS serves requests; four is the realistic figure, and WSL2’s processors= setting should be 2 so the host keeps two for the Windows side.
When Not to Run WSL2 on the Host
If the server’s job is to serve production traffic, run the build elsewhere. The cost of WSL2 on a production host is not the idle 1 GB — it is the unbounded page cache growth during a build, which manifests on the Windows side as IIS request queueing and SQL Server memory grants waiting. A separate build agent or a container host removes the interference entirely. If you must co-locate, cap memory= at 25% of installed RAM, set autoMemoryReclaim=gradual in .wslconfig (WSL 2.0+), and keep the swap file off the system volume. Compare tiers on the Windows Server hosting plans against that 14 GB floor before you commit, since upgrading RAM later usually means a reboot and, on some providers, a migration.
