The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

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

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

Image

由於 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) 與快取。

    # 清理 BuildKit 的建構快取 (Build Cache)
    docker builder prune -a
    
    # 清理整個系統中不被使用的資源 (包含容器、映像檔、網路、快取等)
    # 這會刪除所有已停止的容器與未使用的映像檔,請確認後在執行
    # docker system prune -a
    
  2. 進入 WSL 並清理套件快取,或刪除不需要的大型檔案

    # 清理 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 來標記未使用的區塊,此指令會通知底層虛擬化層哪些區塊不再使用。

    sudo fstrim -v /
    

    sudo fstrim -v /

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

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

  1. 找到 ext4.vhdx 的路徑:通常位於使用者的 AppData 資料夾中。

    通常檔名都叫做 ext4.vhdx,你可以透過 Everything 快速找到此檔案。

    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 運作中的實體:

    wsl --shutdown
    

    然後再執行一次 wsl --list --verbose 命令,確認狀態皆為 Stopped 狀態。

步驟三:執行壓縮

  1. 以系統管理員身分開啟 PowerShell 或 CMD

  2. 輸入 diskpart 命令,並按 Enter 進入互動模式

  3. 依序執行以下指令

    選擇虛擬磁碟檔案 (請替換為您的實際檔案路徑)

    select vdisk file="C:\Users\USER\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu22.04LTS_79rhkp1fndgsc\LocalState\ext4.vhdx"
    

    以唯讀模式掛載 (避免資料損壞)

    attach vdisk readonly
    

    執行壓縮

    compact vdisk
    

    卸載虛擬磁碟

    detach vdisk
    

    退出 Diskpart

    exit
    

    Image

  4. 執行完畢後,您應該會發現檔案總管中的 ext4.vhdx 檔案大小已顯著減少。

自動化作業

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

@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 檔案。

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 環境的路徑,可以自行更改。

留言評論