<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ disk - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ disk - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sat, 30 May 2026 19:49:54 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/disk/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Free Up and Automatically Manage Disk Space for WSL on Windows 10/11 ]]>
                </title>
                <description>
                    <![CDATA[ Windows Subsystem for Linux (WSL) lets you run a Linux environment directly on Windows. This is particularly useful for web development where you can develop and test applications in a Linux environme ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-free-up-and-automatically-manage-disk-space-for-wsl-on-windows-1011/</link>
                <guid isPermaLink="false">6893e671640b08f689368ee6</guid>
                
                    <category>
                        <![CDATA[ WSL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ wsl2 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ disk management ]]>
                    </category>
                
                    <category>
                        <![CDATA[ disk ]]>
                    </category>
                
                    <category>
                        <![CDATA[ disk space ]]>
                    </category>
                
                    <category>
                        <![CDATA[ automation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Powershell ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Windows ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Windows 10 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ windows 11 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ brooklyn ]]>
                </dc:creator>
                <pubDate>Wed, 06 Aug 2025 23:34:09 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1754523230294/70893973-fddf-42a9-b41a-2a8f94a47e22.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Windows Subsystem for Linux (<a href="https://learn.microsoft.com/en-us/windows/wsl/install">WSL</a>) lets you run a Linux environment directly on Windows. This is particularly useful for web development where you can develop and test applications in a Linux environment without leaving Windows. You can even run <a href="https://contribute.freecodecamp.org/how-to-setup-wsl/">freeCodeCamp locally</a> with it!</p>
<p>But managing disk space can be a quite a challenge, as WSL uses virtual hard disks that do not automatically free up unused space.</p>
<p>This tutorial will guide you through the process of manually compacting your WSL virtual hard disks. We’ll automate this task using a PowerShell script, ensuring that your WSL environment remains efficient and clutter-free.</p>
<h2 id="heading-reclaim-your-space">Reclaim Your Space</h2>
<p>WSL uses a virtualization platform to install Linux distributions on your Windows system. Each distribution you add gets its own Virtual Hard Disk (VHD), which uses the ext4 file system (common in Linux). It’s saved on your Windows drive as an ext4.vhdx file.</p>
<p>Key issues here:</p>
<ul>
<li><p>Inefficient storage: by default, VHD files <strong>do not reclaim</strong> unused space. This means that when you delete a file in WSL, the associated disk space isn’t immediately freed up.</p>
</li>
<li><p>Disk space consumption: due to that inefficient storage, the <strong>VHD files can grow large</strong> thanks to that accumulated data, especially if you’re a WSL heavy user.</p>
</li>
<li><p>Need for maintenance: you may not know that you need to <strong>compact</strong> your VHD files in order to reclaim disk space.</p>
</li>
</ul>
<p>If you notice that your free disk space is shrinking even after deleting files and apps, WSL might be the reason. This tutorial will help you keep your WSL and Windows environment running smoothly.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-part-1-how-to-manually-compact-your-virtual-hard-disk">Part 1: How to Manually Compact Your Virtual Hard Disk</a></p>
<ul>
<li><p><a href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a href="#heading-step-1-verify-your-wsl-version-and-status">Step 1: Verify your WSL version and status</a></p>
</li>
<li><p><a href="#heading-step-2-list-all-installed-distributions-verbosely">Step 2: List all installed distributions verbosely</a></p>
</li>
<li><p><a href="#heading-step-3-locate-your-linux-virtual-hard-drive-vhdx-path">Step 3: Locate your linux Virtual Hard Drive (VHDX) path</a></p>
</li>
<li><p><a href="#heading-step-4-shut-down-all-wsl-instances">Step 4: Shut down all WSL instances</a></p>
</li>
<li><p><a href="#heading-step-5-compact-the-linux-virtual-hard-drive-using-diskpart">Step 5: Compact the Linux virtual hard drive using DiskPart</a></p>
</li>
<li><p><a href="#heading-step-6-restart-wsl-and-verify">Step 6: Restart WSL and verify</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-part-2-how-to-make-your-life-easier-with-automation">Part 2: How to Make Your Life Easier with Automation</a></p>
<ul>
<li><p><a href="#heading-prerequisites-1">Prerequisites</a></p>
</li>
<li><p><a href="#heading-step-1-find-out-installed-wsl2-distributions">Step 1: Find out installed WSL2 distributions</a></p>
</li>
<li><p><a href="#heading-step-2-select-a-distro-to-compact">Step 2: Select a distro to compact</a></p>
</li>
<li><p><a href="#heading-step-3-locate-the-ext4vhdx-file">Step 3: Locate the ext4.vhdx File</a></p>
</li>
<li><p><a href="#heading-step-4-the-confirmation-prompt">Step 4: The confirmation prompt</a></p>
</li>
<li><p><a href="#heading-step-5-shut-down-wsl-and-compact">Step 5: Shut Down WSL and compact</a></p>
</li>
<li><p><a href="#heading-step-6-run-a-diskpart-script">Step 6: Run a DiskPart script</a></p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-part-1-how-to-manually-compact-your-virtual-hard-disk"><strong>Part 1: How to Manually Compact Your Virtual Hard Disk</strong></h2>
<p>Let's start by going through the process manually. This section will guide you through checking your WSL version and associated Linux distributions, finding VHD files, shutting down WSL, and compacting the virtual disk.</p>
<h3 id="heading-prerequisites"><strong>Prerequisites</strong></h3>
<ul>
<li><p>Windows 10 (20H1/2004+) or Windows 11 with WSL2 installed</p>
</li>
<li><p>The PowerShell or Command Prompt running as <strong>Administrator</strong> (from the Windows menu, right click the icon and choose run as Administrator).</p>
</li>
</ul>
<h3 id="heading-step-1-verify-your-wsl-version-and-status"><strong>Step 1: Verify your WSL version and status</strong></h3>
<p>First, make sure you’re running on WSL version 2 (commonly referred as WSL2). The first version is outdated and WSL2 provides significant improvements. Open PowerShell (as Admin) or Command Prompt (as Admin) and run:</p>
<pre><code class="language-powershell">wsl -v

wsl --status
</code></pre>
<p>These commands display the WSL client version and whether your default distro is using WSL 2. Here’s the output of the <code>wsl -v</code> command:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1754250376279/ef11af4b-ba5b-43f9-9532-db2634eed154.png" alt="Command prompt displaying WSL version 2.5.9.0, with corrupted or incomplete text following &quot;Kernel version:&quot;, &quot;WSLg version:&quot;, and other version labels." width="421" height="143" loading="lazy">

<p>And here’s the output of the <code>wsl --status</code> command.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1754250364365/6cea2d97-0796-4320-8f84-58d1b5e62c5e.png" alt="Command line text showing &quot;C:sers>wsl --status&quot; with the information &quot;Default Distribution: Ubuntu&quot; and &quot;Default Version: 2&quot;." width="243" height="71" loading="lazy">

<h3 id="heading-step-2-list-all-installed-distributions-verbosely"><strong>Step 2: List all installed distributions verbosely</strong></h3>
<p>To see a detailed list of your WSL distributions (including which version each uses), run:</p>
<pre><code class="language-powershell">wsl.exe --list --verbose
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1754250281542/1826814a-5516-483e-b8ab-6477fd950e21.png" alt="Output of  the WSL --list --verbose command." width="302" height="67" loading="lazy">

<p>Above you can see the output of the WSL <code>--list --verbose</code> command.</p>
<p>Look for your distro name (for example, “<em>Ubuntu</em>”) and note its WSL version. If it shows “Version 2”, you can proceed with compaction.</p>
<h3 id="heading-step-3-locate-your-linux-virtual-hard-drive-vhdx-path"><strong>Step 3: Locate your linux Virtual Hard Drive (VHDX) path</strong></h3>
<p>Each WSL distro’s files live in a <a href="https://en.wikipedia.org/wiki/VHD_(file_format)">VHDX file</a> on your Windows drive. To find the path for any Linux distribution, use this PowerShell snippet:</p>
<pre><code class="language-powershell">(Get-ChildItem `

-Path HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss `

| Where-Object { $_.GetValue("DistributionName") -eq 'YOUR_DISTRO_NAME' }

).GetValue("BasePath") + "\ext4.vhdx"
</code></pre>
<p>Where you replace <code>YOUR_DISTRO_NAME</code> with yours (Ubuntu, Debian, Kali-linux..). Here’s the output of the command shown above in PowerShell (the filepath has been anonymized):</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1754251303855/ea3b3880-5804-4f50-97c8-327ffd017084.png" alt="A PowerShell command is displayed, used to locate the ext4.vhdx file for the Ubuntu distribution. The command retrieves the Windows Subsystem for Linux (WSL) base path for Ubuntu." width="616" height="74" loading="lazy">

<p>This command reads the registry key for your linux distribution, then appends “\ext4.vhdx” to build the full file path.</p>
<p>Make sure you copy the whole line. We will need it in later stages.</p>
<h3 id="heading-step-4-shut-down-all-wsl-instances"><strong>Step 4: Shut down all WSL instances</strong></h3>
<p>Before you can compact any virtual drive, make sure WSL is completely shut down. In PowerShell or Command Prompt (still as Administrator), run:</p>
<pre><code class="language-powershell">wsl.exe --shutdown
</code></pre>
<h3 id="heading-step-5-compact-the-linux-virtual-hard-drive-using-diskpart"><strong>Step 5: Compact the Linux virtual hard drive using DiskPart</strong></h3>
<p>You successfully gathered all the needed information (about your system, the available distros, and their VHDX filepath) to proceed with the main task. In this step, you actually proceed with the compaction.</p>
<ol>
<li>Launch DiskPart in the same elevated (<em>admin</em>) shell:</li>
</ol>
<pre><code class="language-powershell">diskpart
</code></pre>
<p>DiskPart will open in a new window. It's a Windows command-line tool for managing disk partitions. Be cautious when using it, as incorrect actions can cause serious data loss.</p>
<ol>
<li>In the DiskPart prompt, select the VHDX file you found earlier. Replace the path as displayed below with your actual path (the line you copied before):</li>
</ol>
<pre><code class="language-powershell">select vdisk file="C:\Users\username\AppData\path\to\ext4.vhdx"
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1754251748072/91795798-0896-4fcc-994c-0ab311955bee.png" alt="Screenshot of a command prompt window showing Microsoft DiskPart version information. A command is entered to select a virtual disk file, and a message confirms successful selection." width="920" height="150" loading="lazy">

<p>The above is the output of the select vdisk command (some data has been anonymized).</p>
<ol>
<li>Attach the virtual drive in read-only mode:</li>
</ol>
<p>Compaction only needs to scan the empty blocks in the file, not write to the Linux filesystem inside. Read-only mode guarantees that DiskPart only inspect the blocks for zero‐trimming without any chance of damaging or altering your Linux filesystem.</p>
<pre><code class="language-powershell">attach vdisk readonly
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1754251937734/a90af720-1511-42cc-bc37-63439d855907.png" alt="Command prompt showing the output for &quot;DISKPART> attach vdisk readonly&quot; with a successful attachment message." width="462" height="107" loading="lazy">

<p>You can see in the screenshot above that the virtual hard drive has been successfully attached.</p>
<ol>
<li>Compact the disk:</li>
</ol>
<p>This frees up the disk space by shrinking the physical size of the <code>.vhdx</code> file to match the <strong>actual used data</strong> inside.</p>
<pre><code class="language-powershell">compact vdisk
</code></pre>
<p>This operation might take a while. When you see the <code>“DiskPart successfully compacted the virtual disk file”</code> message, proceed with the next step. In the image below, the virtual hard drive has been successfully compacted.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1754252148605/51cc9739-775b-4a84-a526-7c2a6d2a9722.png" alt="Terminal output showing &quot;DISKPART> compact vdisk&quot; with &quot;100 percent completed,&quot; indicating successful compaction of the virtual disk file." width="450" height="96" loading="lazy">

<ol>
<li>Detach the virtual drive:</li>
</ol>
<pre><code class="language-powershell">detach vdisk
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1754252460797/e29387d7-4f03-4ddb-80f7-f14a5051b0fe.png" alt="Command prompt showing &quot;DISKPART> detach vdisk&quot; and a confirmation message that DiskPart successfully detached the virtual disk file." width="446" height="72" loading="lazy">

<p>There you go – the virtual hard drive has been successfully detached.</p>
<p>This command releases any locks on the virtual drive and effectively dismounts it. If you don't use this command, the file remains "in use," preventing WSL (or you) from accessing it until you reboot or manually force it closed.</p>
<p>6. Exit DiskPart:</p>
<pre><code class="language-powershell">exit
</code></pre>
<h3 id="heading-step-6-restart-wsl-and-verify"><strong>Step 6: Restart WSL and verify</strong></h3>
<p>Back in PowerShell or Command Prompt, you can relaunch your distro:</p>
<pre><code class="language-powershell">wsl -d YOUR_DISTRO_NAME
</code></pre>
<p>You can even try the Unix <code>df -h</code> command in your WSL prompt to check your new available disk spaces.</p>
<p>Congrats, you just achieved a maintenance task that can free up lots of gigabytes of storage over time. Now, it’s time to automate.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1754321783829/4325a626-806e-47e7-9147-a76f4c91a93a.jpeg" alt="A minimalistic white rectangular button with a cable on a pink surface." style="display:block;margin:0 auto" width="640" height="360" loading="lazy">

<h2 id="heading-part-2-how-to-make-your-life-easier-with-automation"><strong>Part 2: How to Make Your Life Easier with Automation</strong></h2>
<p>Since it's often hard to remember exactly where your WSL distro is located and you probably won't use it very often, this PowerShell script will automate the entire process we covered in part 1. Here's a preview of the steps you'll follow:</p>
<ul>
<li><p>Detect installed WSL distributions.</p>
</li>
<li><p>Select one (and handle the cases there are more than one).</p>
</li>
<li><p>Locate the corresponding <code>ext4.vhdx</code> file.</p>
</li>
<li><p>Shut down WSL and use DiskPart to compact the virtual disk.</p>
</li>
</ul>
<h3 id="heading-prerequisites"><strong>Prerequisites</strong></h3>
<ul>
<li><p>Windows 10 (20H1/2004+) or Windows 11 with WSL 2 enabled.</p>
</li>
<li><p>PowerShell or Command Prompt (as Administrator).</p>
</li>
</ul>
<p>You’ll also need a code editor. The Windows notepad is enough for completing this task. You can also use an IDE (Integrated Development Environment) like VS Code or an ISE (Integrated Scripting Environment) like PowerShell ISE (included with Windows).</p>
<p>To test the script, download it from <a href="https://github.com/hyperphantasia/WSL-VHDX-Compact/blob/c5c2e346ab0dd1a8dbc6130f8d372af8022ddd60/wsl_compactor.ps1">GitHub</a>. Open an elevated PowerShell or Command Prompt and navigate to the script’s folder. With just the command below, you will be able to run it and free up some disk space:</p>
<pre><code class="language-powershell">powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\wsl_compactor.ps1
</code></pre>
<h3 id="heading-step-1-find-out-installed-wsl2-distributions"><strong>Step 1: Find out installed WSL2 distributions</strong></h3>
<p>One of the main challenges is to find the Linux distributions available on the host system. Let’s check the first block and see what’s it about:</p>
<pre><code class="language-powershell">$lxssKey = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss'
\(distros = Get-ChildItem \)lxssKey | ForEach-Object {
    \(p = Get-ItemProperty \)_.PSPath
    [PSCustomObject]@{
        Name     = $p.DistributionName
        BasePath = $p.BasePath
    }
}
</code></pre>
<p>WSL records each distribution under this windows registry key:</p>
<p><code>HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss</code></p>
<p>Each subkey has two important values:</p>
<ul>
<li><p><strong>DistributionName</strong> (for example, Ubuntu)</p>
</li>
<li><p><strong>BasePath</strong> (This is where the distribution files are stored. It’s the directory that contains the <code>ext4.vhdx</code> file.)</p>
</li>
</ul>
<p>The script uses <code>Get-ChildItem</code> and <code>Get-ItemProperty</code> to enumerate these subkeys and build a list of available Linux distributions.</p>
<pre><code class="language-powershell">if ($distros.Count -eq 0) {
Throw-And-Exit "No WSL distros found in the registry."
}
</code></pre>
<p>If no distributions are found, the script terminates and prints this error message on the terminal: <code>"No WSL distros found in the registry.”</code></p>
<h3 id="heading-step-2-select-a-distro-to-compact"><strong>Step 2: Select a distro to compact</strong></h3>
<p>Here, the process has two steps:</p>
<ul>
<li>If multiple distros are found, it displays all the distros with a numbered menu and prompts you to choose one:</li>
</ul>
<pre><code class="language-powershell">if ($distros.Count -gt 1) {
    Write-Host "Multiple distros detected. Please choose one:`n"

    for (\(i = 0; \)i -lt \(distros.Count; \)i++) {
        Write-Host "[\((\)i+1)] \((\)distros[$i].Name)"
    }
    \(selected = \)distros[[int]$choice - 1]
}
</code></pre>
<p>The computed menu will look like this:</p>
<pre><code class="language-markdown">Multiple distros detected. Please choose one:

[1] Ubuntu 20.04

[2] Debian

[3] Alpine
</code></pre>
<ul>
<li>If only one distribution is found on the host system, the script selects it automatically:</li>
</ul>
<pre><code class="language-powershell">else {
\(selected = \)distros[0]
}
</code></pre>
<p>When setting up a distribution, whether chosen manually by the user or selected automatically, <strong>the important information is the path to each distribution's virtual hard disk</strong>. This path is saved in two main variables: <code>'distro'</code> (which identifies the specific distribution) and <code>'basePath'</code> (which shows where its virtual disk is located).</p>
<pre><code class="language-powershell">\(distro = \)selected.Name
\(basePath = \)selected.BasePath

Write-Host "`nSelected distro: $distro" -ForegroundColor DarkYellow
Write-Host "BasePath: $basePath"
</code></pre>
<p>The lines above display an output that looks like this:</p>
<pre><code class="language-markdown">Selected distro: Ubuntu (or any other distro)
BasePath: C:\Users\&lt;User_name&gt;\AppData\Local\Packages\…
</code></pre>
<p>Like for all other steps, it’s important to consider the case of something going wrong, by throwing an error and exiting the program:</p>
<pre><code class="language-powershell">if (-not (Test-Path $basePath)) {
Throw-And-Exit "BasePath '$basePath' does not exist on disk."
}
</code></pre>
<h3 id="heading-step-3-locate-the-ext4vhdx-file"><strong>Step 3: Locate the ext4.vhdx File</strong></h3>
<p>In the first step, we collected the information we need about the available distributions and where they are stored on the Windows system. By choosing an entry (either manually or automatically), we can find the correct file. Sometimes, the ext4 file is located between the base path and a <em>LocalState</em> folder. This script manages both situations. It builds the usual locations where the file can be found.</p>
<p>They look like this:</p>
<ul>
<li><p><code>$BasePath\ext4.vhdx</code></p>
</li>
<li><p><code>$BasePath\LocalState\ext4.vhdx</code></p>
</li>
</ul>
<p>This can translate into something like this on your system (option 1):</p>
<pre><code class="language-markdown">C:\Users\Alice\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu20.04onWindows_79rhkp1fndgsc\ext4.vhdx
</code></pre>
<p>or like this (option 2):</p>
<pre><code class="language-markdown">C:\Users\Alice\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu20.04onWindows_79rhkp1fndgsc\LocalState\ext4.vhdx
</code></pre>
<p>(You might find out that your WSL2 distro is located in some other directory than “Packages” – but don’t worry, your BasePath will match the correct folders).</p>
<p>The idea is to build the two possible path options:</p>
<pre><code class="language-powershell">$possible = @(
Join-Path $basePath 'ext4.vhdx'
Join-Path $basePath 'LocalState\ext4.vhdx'
)
</code></pre>
<p>And pick the first one that actually contains the file:</p>
<pre><code class="language-powershell">\(vhdx = \)possible | Where-Object { Test-Path $_ } | Select-Object -First 1
</code></pre>
<p>Again, we throw an error message if no suitable file is found:</p>
<pre><code class="language-powershell">if (-not $vhdx) {
Throw-And-Exit "No ext4.vhdx found under '$basePath'."
}
</code></pre>
<h3 id="heading-step-4-the-confirmation-prompt"><strong>Step 4: The confirmation prompt</strong></h3>
<p>Disk management tools require caution and you need to understand the potential consequences of your actions. A confirmation prompt is always a good safeguard to prevent accidental data loss or unwanted system changes.</p>
<p>Before proceeding, the script shows you:</p>
<ul>
<li><p>a Distro name</p>
</li>
<li><p>its BasePath</p>
</li>
<li><p>the VHDX file path</p>
</li>
</ul>
<pre><code class="language-powershell">Write-Host "`nAbout to compact this WSL distro:" -ForegroundColor Magenta
Write-Host " Distro : $distro"
Write-Host " BasePath : $basePath"
Write-Host " VHDX file: $vhdx`n"
</code></pre>
<p>It then prompts <strong>“Are you sure you want to proceed? (Y/N)”:</strong></p>
<pre><code class="language-powershell">Write-Host "Are you sure you want to proceed? (Y/N) " -ForegroundColor DarkCyan -NoNewline

# Then read the response
$answer = Read-Host
</code></pre>
<p>You’re then prompted to Type Y (case-insensitive) to continue or anything else to cancel.</p>
<pre><code class="language-powershell">if ($answer.ToUpper() -ne 'Y') {
    Write-Warning "Operation canceled"
    exit
}
</code></pre>
<p>For the two steps above, I had to use a trick to print the question in color but a simple option (without colors) could be:</p>
<pre><code class="language-powershell">if ((Read-Host 'Are you sure you want to proceed? (Y/N)').ToUpper() -ne 'Y') { 
    Write-Warning 'Operation canceled'
    exit 
}
</code></pre>
<h3 id="heading-step-5-shut-down-wsl-and-compact"><strong>Step 5: Shut down WSL and compact</strong></h3>
<p>Before proceeding to the DiskPart utility, it’s important to stop all running WSL instances. Pass the shutdown command directly in PowerShell.</p>
<pre><code class="language-powershell">Write-Host "Shutting down WSL…" -ForegroundColor Cyan
wsl.exe –shutdown
</code></pre>
<p>A common mistake is to forget to launch PowerShell or the Command Prompt with Administrator rights. You can prevent this case with a message:</p>
<pre><code class="language-powershell">if ($LASTEXITCODE -ne 0) {
     Throw-And-Exit "Failed to shut down WSL (exit code $LASTEXITCODE). Are you running as Administrator?"
}
</code></pre>
<h3 id="heading-step-6-run-a-diskpart-script"><strong>Step 6: Run a DiskPart script</strong></h3>
<h4 id="heading-building-the-script">Building the script:</h4>
<p>The process is the same as in the manual part, but this time, we ‘inject’ the ready-to-go DiskPart commands into the script.</p>
<pre><code class="language-powershell">$dpScript = @"
select vdisk file="$vhdx"
attach vdisk readonly
compact vdisk
detach vdisk
exit
"@
</code></pre>
<p>Before launching, there are two steps you need to take:</p>
<ol>
<li>The PowerShell script writes the lines above to a temporary file:</li>
</ol>
<pre><code class="language-powershell">$tempFile = [IO.Path]::GetTempFileName()
Set-Content -LiteralPath \(tempFile -Value \)dpScript -Encoding ASCII
</code></pre>
<p>This is the equivalent to the commands passed in the manual part:</p>
<p><code>select vdisk file="</code><a href="/home/C:/"><code>C:\</code></a><code>…\ext4.vhdx" # full path to the vdisk file</code></p>
<p><code>attach vdisk readonly</code></p>
<p><code>compact vdisk</code></p>
<p><code>detach vdisk</code></p>
<p><code>exit</code></p>
<ol>
<li>Compacting can take a while, especially if you’ve never de-cluttered your virtual drive before. It’s wise to show a warning before proceeding:</li>
</ol>
<pre><code class="language-powershell">Write-Host "Running DiskPart to compact the VHDX. Be patient, this might take a while..." -ForegroundColor Cyan
</code></pre>
<h4 id="heading-invoke-diskpart"><strong>Invoke DiskPart:</strong></h4>
<pre><code class="language-powershell"># Run DiskPart with the script saved to the temporary file and process each output line as it arrives
diskpart /s $tempFile | ForEach-Object {
    # Grab any "NN percent" type message from the line
    if ($_ -match '(\d+)\s+percent') {
        # Only print when the percentage actually changes
        Write-Host "\((\)Matches[1])% completed"
    }
    else {
        # Just echo all over line-types, verbatim
        Write-Host $_
    }
}
</code></pre>
<p>Several points to note here:</p>
<ul>
<li><p>It runs <code>diskpart /s $tempFile</code>: DiskPart reads and executes commands from the temporary file into the PowerShell loop for on-the-fly processing.</p>
</li>
<li><p>For a better user-experience: the snippet below does the trick of filtering out repeated status values by comparing <code>\(pct</code> with the sentinel <code>\)lastPct</code>, and only writing new lines when they differ.</p>
</li>
</ul>
<p>How?</p>
<p>Before entering the loop, we initialize:</p>
<pre><code class="language-powershell">$lastPct with -1
$lastPct = -1 # We initiate a sentinel value
</code></pre>
<p>We are having a guaranteed “first” value that no real percent (0–100) will equal. That way, as soon as you see the first 0 percent, 10 percent, or whatever, it differs from -1.</p>
<p>Then:</p>
<pre><code class="language-powershell">if ($_ -match '(\d+)\s+percent') {
    # Print only when the percentage changes
    Write-Host "\((\)Matches[1])% completed"
}
</code></pre>
<p>This guarantees that on the very first percentage update (say “0 percent” or “10 percent”), <code>\(pct –ne \)lastPct</code> will be <code>true</code>, so it emits the first line. Afterwards, <code>$lastPct</code> holds the last real percentage, and it only prints again when a new, different progress percentage comes in.</p>
<p>The output looks more clean:</p>
<pre><code class="language-markdown">10% completed
20% completed
…
</code></pre>
<p>Otherwise, it’ll flood the screen with dozens of identical “20 percent completed” (for example) updates.</p>
<p>Of course we handle the case of other values (non percent lines) normally:</p>
<pre><code class="language-powershell">else {
    # non-percent lines: print verbatim
    if ($_ -match '\S') {
        Write-Host $_
    }
}
</code></pre>
<p>Once the process is done, don’t forget to clean up the tempfile.</p>
<pre><code class="language-powershell">Remove-Item $tempFile -ErrorAction SilentlyContinue
</code></pre>
<p>By the end of the process, you should see something like this</p>
<pre><code class="language-markdown">Leaving DiskPart...
</code></pre>
<p>Okay, that’s it for the scripting! If you collected all the snippets so far, just save them with a <code>.ps1</code> file extension, or download the full example from this <a href="https://github.com/hyperphantasia/WSL-VHDX-Compact">GitHub repository</a>.</p>
<h3 id="heading-usage"><strong>Usage:</strong></h3>
<p>You now have a complete understanding of what's happening. Ready to get started? In Windows, open the Command Prompt or PowerShell <strong>as an administrator.</strong></p>
<ul>
<li><p>Navigate to the directory containing your <code>.ps1</code>&nbsp;script.</p>
</li>
<li><p>Execute the script with the following command, replacing <code>&lt;File_name_here&gt;</code> with your actual file name:</p>
</li>
</ul>
<pre><code class="language-powershell">powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\&lt;File_name_here&gt;.ps1
</code></pre>
<p>The <code>-NoProfile -ExecutionPolicy Bypass</code> parameters launch PowerShell in a clean, unrestricted environment that ignores user-specific settings and allows script execution without security restrictions. Don’t worry, in this case, it’s okay to do that.</p>
<p><em>Wait…wait…wait...</em></p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1754315563256/a709192a-6626-4a12-b411-46c8591c5eb6.jpeg" alt="Screenshot of a command line interface showing the execution of a PowerShell script to compact a WSL (Windows Subsystem for Linux) distro. The script confirms the selected distro &quot;Ubuntu&quot; and proceeds to compact the VHDX file using DiskPart. Progress is shown in percentage increments, with final messages indicating successful completion of the compacting process." style="display:block;margin:0 auto" width="878" height="500" loading="lazy">

<p>Well bravo! You’ve just reclaimed all that unused space inside your WSL2 (almost) hands free!</p>
<p>Now, you can take it a step further by modifying this script to run completely automatically, without the confirmation prompt (step 4). You can also schedule it as a regular maintenance task using a task scheduler:</p>
<pre><code class="language-powershell">schtasks /create /tn "Schedule_name" /tr "powershell.exe -ExecutionPolicy Bypass -File C:\path\to\script.ps1" /sc monthly /d 15 /st 09:00
</code></pre>
<p>This is an example for a monthly execution where <code>/d 15</code> means 15th of each month and <code>/st 09:00</code> is a start time set at 9 am.</p>
<p>That's it! Remember, regular maintenance, whether you do it manually or automatically, is essential to prevent unnecessary disk space usage and ensure a smooth experience with WSL.</p>
<h3 id="heading-thanks-for-reading">Thanks for reading!</h3>
<p>You can find a list of my current projects on <a href="https://github.com/hyperphantasia?tab=repositories">GitHub</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
