# 如何正確對 WSL 2 的 ext4.vhdx 虛擬硬碟進行壓縮

我自從將 Docker 都改裝到 WSL 2 之後，磁碟用量日漸增加，發現 WSL 2 的 `ext4.vhdx` 虛擬硬碟檔案變得非常大，所以又重新研究了一下如何對 WSL 2 的虛擬硬碟進行壓縮，這篇文章就來說明如何正確對 WSL 2 的 `ext4.vhdx` 虛擬硬碟進行壓縮。

![Image](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/3225139e681e664377ee-f39c377e-36c8-4211-94a1-9908b7fbf888.webp)

由於 WSL 2 的虛擬硬碟採用**動態擴充**機制，它會隨著寫入資料而變大，但在**刪除資料後不會自動釋放空間**回 Windows 系統，因此必須手動執行壓縮 (Compact) 操作。

若要壓縮 WSL 2 或 Docker Desktop 使用的 `ext4.vhdx` 虛擬硬碟檔案，主要有兩種方式：

1.  使用 **Hyper-V** 的 PowerShell 指令 `Optimize-VHD`
    
    如果你使用的是 Windows 專業版、企業版或教育版，並且已啟用 Hyper-V 功能，才能使用 `Optimize-VHD` 指令會更快速且方便。
    
2.  使用 **Windows** 內建的 `Diskpart` 工具
    
    這是 Windows 內建的磁碟管理工具，適用於 Windows Home 與 Pro 版本，是最為通用的方法，本篇文章會以這個方法為主。
    

### 步驟一：清理 Linux/Docker 內部的資料

在壓縮虛擬硬碟之前，必須先**在系統內部釋放空間**，**標記未使用的區塊**。否則，外部工具無法識別哪些空間可以回收。

1.  先清除未使用的 Docker 映像檔 (Images)、容器 (Containers) 與快取。
    
    ```bash
    # 清理 BuildKit 的建構快取 (Build Cache)
    docker builder prune -a
    
    # 清理整個系統中不被使用的資源 (包含容器、映像檔、網路、快取等)
    # 這會刪除所有已停止的容器與未使用的映像檔，請確認後在執行
    # docker system prune -a
    ```
    
2.  進入 WSL 並清理套件快取，或刪除不需要的大型檔案
    
    ```bash
    # 清理 apt 套件管理器的快取與不需要的套件
    sudo apt-get clean
    sudo apt-get autoclean
    sudo apt-get autoremove
    
    # 刪除 Node.js 的套件快取
    npm cache clean --force
    yarn cache clean
    
    # 刪除 Rust 的套件快取
    rm -rf ~/.cargo/registry/cache/*
    
    # 你可以從 ~/.cache/ 目錄下找到許多可能已經不需要的快取檔案！
    ```
    
    這些指令會清除 apt 套件管理器的快取與不需要的套件。
    
3.  從 WSL 標記未使用的區塊
    
    在進行檔案清理之後，建議執行 `fstrim` 來標記未使用的區塊，此指令會**通知底層虛擬化層**哪些區塊不再使用。
    
    ```bash
    sudo fstrim -v /
    ```
    
    ![sudo fstrim -v /](https://stwillblogassets.blob.core.windows.net/files/images/external/github.com/552cefad402f32dee658-14bb9c2f-6d30-4d49-93f4-db9e5c5a1b7a.png)
    

### 步驟二：確認檔案路徑並關閉 WSL

在操作 `ext4.vhdx` 檔案之前，必須確保沒有任何處理序 (Process) 正在使用它。

1.  找到 `ext4.vhdx` 的路徑：通常位於使用者的 `AppData` 資料夾中。
    
    通常檔名都叫做 `ext4.vhdx`，你可以透過 [Everything](https://www.voidtools.com/) 快速找到此檔案。
    
    Ubuntu (WSL 2) 常見路徑如下：
    
    -   Ubuntu 18.04: "%LOCALAPPDATA%\\Packages\\CanonicalGroupLimited.Ubuntu18.04onWindows\_79rhkp1fndgsc\\LocalState\\ext4.vhdx"
    -   Ubuntu 20.04: "%LOCALAPPDATA%\\Packages\\CanonicalGroupLimited.Ubuntu20.04onWindows\_79rhkp1fndgsc\\LocalState\\ext4.vhdx"
    -   Ubuntu 22.04: "%LOCALAPPDATA%\\Packages\\CanonicalGroupLimited.Ubuntu22.04LTS\_79rhkp1fndgsc\\LocalState\\ext4.vhdx"
    -   Ubuntu 24.04: "%LOCALAPPDATA%\\wsl{a886d7a7-be06-47f7-bc0f-9c4e00ecd51f}\\ext4.vhdx"
    
    > 請自行將 `%LOCALAPPDATA%` 替換為實際路徑：`C:\Users\USERNAME\AppData\Local`
    
2.  確保所有 WSL 實體都已停止運作
    
    你可以用 `wsl --list --verbose` 指令來確認所有 WSL 實體的狀態，確保它們都是停止 (Stopped) 狀態。
    
    接著你可以透過以下命令完全關閉 WSL 運作中的實體：
    
    ```bash
    wsl --shutdown
    ```
    
    然後再執行一次 `wsl --list --verbose` 命令，確認狀態皆為 `Stopped` 狀態。
    

### 步驟三：執行壓縮

1.  以系統管理員身分開啟 PowerShell 或 CMD
    
2.  輸入 `diskpart` 命令，並按 Enter 進入互動模式
    
3.  依序執行以下指令
    
    選擇虛擬磁碟檔案 (請替換為您的實際檔案路徑)
    
    ```batch
    select vdisk file="C:\Users\USER\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu22.04LTS_79rhkp1fndgsc\LocalState\ext4.vhdx"
    ```
    
    以唯讀模式掛載 (避免資料損壞)
    
    ```batch
    attach vdisk readonly
    ```
    
    執行壓縮
    
    ```batch
    compact vdisk
    ```
    
    卸載虛擬磁碟
    
    ```batch
    detach vdisk
    ```
    
    退出 Diskpart
    
    ```batch
    exit
    ```
    
    ![Image](https://stwillblogassets.blob.core.windows.net/files/images/external/github.com/b534b62e3942d6d76724-6fca044e-ccb1-49cc-a7b2-be8a272771c5.png)
    
4.  執行完畢後，您應該會發現檔案總管中的 `ext4.vhdx` 檔案大小已顯著減少。
    

### 自動化作業

我將上述步驟寫成一個 `CompactVHDX.bat` 批次檔，方便日後重複使用：

```batch
@echo off
setlocal EnableDelayedExpansion

:: ========================================================
:: 設定 VHDX 檔案路徑 (請在此處修改為您的實際路徑)
:: 注意：路徑中若有空格，請保留引號
:: ========================================================
:: 常見 WSL 2 發行版路徑：
:: - Ubuntu 18.04: %LOCALAPPDATA%\Packages\CanonicalGroupLimited.Ubuntu18.04onWindows_79rhkp1fndgsc\LocalState\ext4.vhdx
:: - Ubuntu 20.04: %LOCALAPPDATA%\Packages\CanonicalGroupLimited.Ubuntu20.04onWindows_79rhkp1fndgsc\LocalState\ext4.vhdx
:: - Ubuntu 22.04: %LOCALAPPDATA%\Packages\CanonicalGroupLimited.Ubuntu22.04LTS_79rhkp1fndgsc\LocalState\ext4.vhdx
:: - Ubuntu 24.04: %LOCALAPPDATA%\wsl\{GUID}\ext4.vhdx
:: ========================================================
set "VHDX_PATH=%LOCALAPPDATA%\Packages\CanonicalGroupLimited.Ubuntu22.04LTS_79rhkp1fndgsc\LocalState\ext4.vhdx"

:: 檢查檔案是否存在
if not exist "%VHDX_PATH%" (
    echo [錯誤] 找不到檔案：
    echo %VHDX_PATH%
    echo.
    echo 請編輯此批次檔並修正 VHDX_PATH 變數。
    echo 提示：您可以使用 Everything 搜尋工具快速找到 ext4.vhdx 檔案位置。
    pause
    exit /b
)

echo ========================================================
echo 正在準備壓縮 WSL2/Docker 虛擬硬碟...
echo 目標檔案: %VHDX_PATH%
echo ========================================================
echo.
echo [提示] 建議在執行此腳本前，先進入 WSL 執行以下清理命令：
echo        docker builder prune -a
echo        sudo apt-get clean ^&^& sudo apt-get autoclean ^&^& sudo apt-get autoremove
echo        sudo fstrim -v /
echo ========================================================

:: 1. 強制關閉 WSL (確保檔案未被鎖定)
echo [1/4] 正在關閉 WSL 服務...
wsl --shutdown
timeout /t 3 /nobreak >nul

:: 2. 建立 Diskpart 暫存腳本
echo [2/4] 正在建立 Diskpart 指令腳本...
set "SCRIPT_FILE=%TEMP%\diskpart_compact_script.txt"

(
    echo select vdisk file="%VHDX_PATH%"
    echo attach vdisk readonly
    echo compact vdisk
    echo detach vdisk
    echo exit
) > "%SCRIPT_FILE%"

:: 3. 執行 Diskpart (需要系統管理員權限)
echo [3/4] 正在執行壓縮 (這可能需要幾分鐘)...
echo --------------------------------------------------------
diskpart /s "%SCRIPT_FILE%"
echo --------------------------------------------------------

:: 4. 清理暫存檔
echo [4/4] 清理暫存檔案...
if exist "%SCRIPT_FILE%" del "%SCRIPT_FILE%"

echo ========================================================
echo 壓縮作業完成！
echo ========================================================
pause
```

### 其他替代方案

若上述方法效果不彰，可以考慮使用 `wsl --export` 將發行版匯出成 `tar` 檔，刪除舊環境後，再使用 `wsl --import` 匯入，這會建立一個全新的、最小化的 vhdx 檔案。

```batch
wsl --export Ubuntu-22.04 ubuntu22.04.tar --format tar

wsl --unregister Ubuntu-22.04

wsl --import Ubuntu-22.04 C:\WSL\Ubuntu-22.04\ ubuntu22.04.tar --version 2
```

> 這裡的 `C:\WSL\Ubuntu-22.04\` 是你想要存放新 WSL 2 環境的路徑，可以自行更改。
