Notes on WSL Tuning

Here I'll store my notes on WSL tuning. I mainly use WSL to do Dev Containers, so I need it to run as smooth as possible. My main problems include: memory and disk size. Let's see what we can do to address the problem.

How big are my virtual hard disks?

The following script will show the name of the distro, the size and the path of the virtual hard disks:

Get-ChildItem -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss\ | ForEach-Object {

  $psPath = (Get-ItemProperty -Path $_.PsPath)
  $distro = $psPath.DistributionName
  $vhdxPath = $psPath.BasePath + "\ext4.vhdx"
  $vhdxSize = (Get-Item $vhdxPath).Length / 1GB

  [PSCustomObject]@{
    DistributionName = $distro
    SizeGB = [math]::Round($vhdxSize, 2).ToString("0.00")
    VHDXPath = $vhdxPath.Replace("\\?\", "")
  }

} | Format-Table -Wrap -Property `
  DistributionName, `
  @{Name='SizeGB'; Expression={$_.SizeGB}; Alignment='right'}, `
  VHDXPath

Now you know what might eat up your hard disk.

Manually shrinking the hard disks

The following script will prune your docker containers and image, iterate your virtual hard disks and perform a shrink:

# make sure we're admin
if (-Not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')) {
    if ([int](Get-CimInstance -Class Win32_OperatingSystem | Select-Object -ExpandProperty BuildNumber) -ge 6000) {
        $CommandLine = "-File `"" + $MyInvocation.MyCommand.Path + "`" " + $MyInvocation.UnboundArguments
        Start-Process -FilePath PowerShell.exe -Verb Runas -ArgumentList $CommandLine
        Exit
    }
}

Write-Host
Write-Host "Pruning containers" -ForegroundColor Blue
docker container prune --force

Write-Host
Write-Host "Pruning images" -ForegroundColor Blue
docker image prune --all --force

wsl --shutdown

Get-ChildItem -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss\ | ForEach-Object {
  $psPath = (Get-ItemProperty -Path $_.PsPath)
  $distro = $psPath.DistributionName
  $vhdxPath = $psPath.BasePath.Replace("\\?\", "") + "\ext4.vhdx"
  $vhdxSize = [Math]::Round((Get-Item $vhdxPath).Length / 1GB, 2).ToString("0.00")

  Write-Host
  Write-Host "Shrinking distro: " -NoNewline -ForegroundColor Blue
  Write-Host $distro -ForegroundColor Yellow
  Write-Host " - before: $vhdxSize"

  try {
    Optimize-VHD -Path $vhdxPath -Mode Full -ErrorAction Stop
    $vhdxSize = [Math]::Round((Get-Item $vhdxPath).Length / 1GB, 2).ToString("0.00")
    Write-Host " - after:  $vhdxSize" -ForegroundColor Green
  } catch {
    if ($_.Exception.Message -match "Virtual hard disk files must be uncompressed and unencrypted and must not be sparse.") {
      Write-Host " - cannot shrink disk as it is already sparse" -ForegroundColor Red
    } else {
      Write-Error "Error: $($_.Exception.Message)"
    exit
    }
  }
}

Write-Host
Read-Host -Prompt "Press Enter to exit"

But you might want to go one further and make those disks sparse!

Stop the bleeding

We can stop the bleeding by configuring WSL2 differently. The September 2023 update includes some great new features:

  • Automatic memory reclaim.
  • Sparse virtual hard disks, which will give the disk space back.
  • Better networking.

Upgrade to the latest release

First you'll need to upgrade to the store version, then to the pre-release version:

wsl --shutdown
wsl --update
wsl --update --pre-release

WSL Config

Next, you might want to now how many (logical) processors and memory you have:

$logicalProcessors = (Get-WmiObject -Class Win32_ComputerSystem).NumberOfLogicalProcessors
$totalMemoryGB = [Math]::Round((Get-WmiObject -Class Win32_ComputerSystem).TotalPhysicalMemory / 1GB, 2)
$output = "Number of Logical Processors: $logicalProcessors"
$output += "`nTotal Memory (GB):            $totalMemoryGB"
Write-Output "$output"

Based on these settings you might want to tune your WSL config. I open it by doing code (Join-Path (Resolve-Path ~) .wslconfig) in a terminal and adding the following to the file:

[wsl2]
memory=10GB
processors=14
guiApplications=false

[experimental]
autoMemoryReclaim=gradual
sparseVhd=true
networkingMode=mirrored
dnsTunneling=true
firewall=true
autoProxy=true

I configured my memory to be ~2/3 of my machine. I keep 2 cores back for the processor config.

Notice how I disabled Linux GUI applications, as it keeps messing up my dev containers with the error: Error response from daemon: invalid mount config for type "bind": bind source path does not exist: \wsl.localhost\Ubuntu\mnt\wslg\runtime-dir\wayland-0. Adding guiApplications=false will fix that.

Settings all your WSL distros to sparse

Now that we've updated and activated our sparse disk, we can apply them to all disks:

wsl --shutdown

Get-ChildItem -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss\ | ForEach-Object {

  $distro = (Get-ItemProperty -Path $_.PsPath).DistributionName

  Write-Host
  Write-Host "Changing distro: " -NoNewline -ForegroundColor Blue
  Write-Host $distro -ForegroundColor Yellow

  wsl --manage $distro --set-sparse true
}

Changelog

  • 2024-04-03: Make sure we disable Wayland / Linux GUI apps to prevent errors on our Dev Containers.
  • 2024-02-20: Fixed the opening of the ~/.wslconfig if it does not exist.
expand_less