auto-mount-vhdx-in-wsl2-ubuntu

WSL2 の Ubuntu に VHDX を自動マウントする

WSL2 Ubuntu のホームディレクトリを別ディスクにしたくなりました。そこで、Windows 上で VHDX 形式のディスクイメージを作り、それを Ubuntu にマウントする方法を探求しました。

  • ホスト: Windows 11 Pro
  • ゲスト: Ubuntu 20.04 と Ubuntu 24.04

ホームディレクトリを別ディスクにしたい理由は、新しい Ubuntu バージョンに移行するにあたり、旧環境の OS をアップグレードするのではなく、新たに新しい Ubuntu の環境を作って使いたいと思ったからです。せっかく WSL2 で複数の VM を作れるのですから、念のため古い環境を残しておきたいです。ホームディレクトリが別ディスクになっていれば、それを新しい VM に接続しなおすだけでデータを引っ越せます。

ディスクイメージの準備

Windows 側で VHD/VHDX ディスクイメージを作り、それを Ubuntu に接続し、Ubuntu 側からパーティションを作り ext4 でフォーマットします。

空のディスクイメージを作成

  1. スタートメニュー右クリック「ディスクの管理」を開きます。
  2. 操作 > VHD の作成 をクリックします。
    • 場所: 任意の場所を指定してください。以降では D:\foo\bar.vhdx に作ったとして説明します。
    • サイズ: 上限値を指定します。
    • フォーマット: VHD でも VHDX でも同じ手順でマウントできると思います。筆者は VHDX を選びました。
    • 種類: お好みで。筆者は可変容量を選びました。
  3. OK をクリックします。

「ディスクの管理」画面の下の方に、今作ったディスクイメージが表示され、「未割り当て」と表示されていることを確認します。ここでパーティションを作ったりしたくなりますが、それは Ubuntu 上でやる方が最適なパーティション配置になるようです。

Windowsのディスクの管理で作成したディスクイメージが未割り当てと表示されている様子

このディスクを右クリックし、「VHD の切断」を選び、このディスクイメージを切り離します。そうしておかないと、後で Ubuntu に接続しようとしたときに「使用中」エラーが出てしまいます。次のようなダイアログが出ます。「OK」をクリックすれば切断できます。

Windowsのディスクの管理にてディスクイメージを切断する様子

今回は GUI でやりましたが、PowerShell を用いてコマンドラインでディスクイメージを作ることも可能なようです。

Ubuntu へディスクイメージを接続

Ubuntu でパーティション作成やフォーマットをするために、手動でディスクイメージを Ubuntu へ接続します。Ubuntu 起動時に自動的に接続する方法は後で紹介します。

管理者モードで PowerShell か cmd.exe を開き、次のコマンドを実行します。

>wsl.exe --mount --bare --vhd D:\foo\bar.vhdx
この操作を正しく終了しました。

オプションを説明します。

オプション 意味
--mount ディスクをすべての WSL 2 ディストリビューションに接続します
--bare マウントはせず、接続だけします
--vhd DISK ディスクイメージを指定します

作成したディスクイメージは ext4 でフォーマットされていないので、まだマウントはできません。--bare を指定し、マウントをしないようにします。

ちなみに、何らかの理由で接続を解除したい場合、次のコマンドで可能です。

>wsl.exe --unmount D:\foo\bar.vhdx

Windows 側の作業はここまでで終わりです。

パーティションを作成

接続したディスクイメージが Ubuntu 側からどんな名前で見えているかを確認します。dmesg を実行し、直近のログを確認します。

$ dmesg
…
[ 9787.207257] sd 0:0:0:4: [sdd] Attached SCSI disk

ここでは sdd というデバイス名で接続されていることが分かります。以降の作業でデバイス名を間違えるとディスク内容が破損するので、慎重に確認してください。不安な場合、先ほど説明したコマンドで接続を解除し dmesg を見れば解除時のログが見えますから、それでデバイス名の再確認をするといいでしょう。接続を解除した場合、パーティション作成の作業の前に改めて接続し直してくださいね。

作成したディスクイメージに対し、Ubuntu 側からパーティションを作り、ext4 でフォーマットします。

今回、パーティション作成には fdisk コマンドを用いました。作業の流れは次の通りです。

  1. g で GPT パーティションテーブルを作成
  2. p でパーティションテーブルを確認(この時点ではパーティションは何も表示されないはず)
  3. n でパーティションを作成(全部デフォルト値にしておくと、作成可能な最大サイズでパーティションが作られる)
  4. p でパーティションテーブルを再確認(今作成したパーティションが表示されるはず)
  5. w で変更をディスクイメージに書く

参考までに、筆者の環境での具体的な表示を貼っておきます。

$ sudo fdisk /dev/sdd
Welcome to fdisk (util-linux 2.39.3).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

Device does not contain a recognized partition table.
Created a new DOS (MBR) disklabel with disk identifier 0xf4ed8ae3.

Command (m for help): g
Created a new GPT disklabel (GUID: 3C53E6CC-62EA-47BC-8672-2521162D7B4C).

Command (m for help): p
Disk /dev/sdd: 1000 MiB, 1048576000 bytes, 2048000 sectors
Disk model: Virtual Disk
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disklabel type: gpt
Disk identifier: 3C53E6CC-62EA-47BC-8672-2521162D7B4C

Command (m for help): n
Partition number (1-128, default 1):
First sector (2048-2047966, default 2048):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-2047966, default 2045951):

Created a new partition 1 of type 'Linux filesystem' and of size 998 MiB.

Command (m for help): p
Disk /dev/sdd: 1000 MiB, 1048576000 bytes, 2048000 sectors
Disk model: Virtual Disk
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disklabel type: gpt
Disk identifier: 3C53E6CC-62EA-47BC-8672-2521162D7B4C

Device     Start     End Sectors  Size Type
/dev/sdd1   2048 2045951 2043904  998M Linux filesystem

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

次に mkfs.ext4 でパーティションをフォーマットします。

$ sudo mkfs.ext4 /dev/sdd1

ディスクイメージの自動マウント設定

ここまでの作業で ext4 でフォーマットされたディスクイメージが準備できました。次に Ubuntu を起動したときに自動的にマウントする設定を行います。

ディスクのマウントといえば /etc/fstab だ、と思ったのですが、今回は違う方法をとります。/etc/fstab を使う方法は、マシンにディスクが接続されている必要がありますが、一度 wsl.exe --shutdown をするとディスクイメージの接続が切れてしまうからです。マシンからディスクが取り外された状態になってしまうということです。

ということで戦略としては、Ubuntu 起動時に何とかして Windows 側へ「ディスクイメージを WSL2 に接続せよ」と指示して、その後 Ubuntu 側からマウントを行うという流れになります。Ubuntu 起動時に自動実行する仕組みはいろいろあるのですが、現代では Systemd でやるのが主流でしょう。ということで、Systemd で oneshot タスクを作ります。

PARTUUID を確認

筆者が試したところ、ディスクイメージが Ubuntu に再接続されるとデバイス名(/dev/sdd)が変わることがありました。これでは安定した自動マウントができませんから、UUID を用いてマウント対象ディスクを固定する方法を用います。ということで、マウントしたいパーティションの PARTUUID を確認します。

$ sudo blkid
…
/dev/sdd1: UUID="4e358cb0-f1a3-430c-b567-4bb905c142ab" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="4ec15969-105c-414a-8940-ba36975a4bde"
…

先ほどフォーマットしたパーティション /dev/sdd1 に対応する PARTUUID が表示されました。これを使うことで、デバイス名が sdg とか sdf とかに変わったとしても問題なくなります。

スクリプトを作成

次に、自動マウントを行うスクリプトを作ります。どこに置いても良いですが、今回は /usr/local/bin/mount_uchan_home.sh としました。

#!/bin/sh

PS="/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe"
PARTUUID="4ec15969-105c-414a-8940-ba36975a4bde"
MNT="/home/uchan"

DISK_IMG='/mnt/d/wsl_diskimg/uchan_home.vhdx'
DISK_FOR=$(cat "${DISK_IMG%.*}.txt")
WSL_DISTRO_NAME=$(wslpath -m / | cut -d/ -f4)
if [ "$DISK_FOR" != $WSL_DISTRO_NAME ]
then
  echo "Disk image is not for me: me=$WSL_DISTRO_NAME for=$DISK_FOR"
  exit 0
fi

if ! mountpoint -q /mnt/uchan_home
then
  "$PS" Start-Process -FilePath wsl.exe -Verb RunAs -ArgumentList "--mount","--bare","--vhd","$(wslpath -w "$DISK_IMG")"
  for i in $(seq 5)
  do
    if blkid | grep -q "PARTUUID=\"$PARTUUID\""
    then
      echo "Attached. then mounting"
      sudo mount -o defaults,noatime PARTUUID="$PARTUUID" "$MNT"
      exit 0
    fi
    echo "Waiting for partition attached: PARTUUID=\"$PARTUUID\""
    echo "blkid=" $(blkid)
    sleep 1
  done
  echo "Give up waiting: PARTUUID=\"$PARTUUID\""
  exit 1
fi

PARTUUID に先ほど表示された値をそのまま設定します。MNT にはマウントポイントを指定します。ここで指定したディレクトリはマウントに先立って存在する必要があります。

DISK_FORDISK_IMG の拡張子を .txt に変更したパスを表します。このファイルは何かというと、中身は「Ubuntu-24.04」など、ディスクイメージをマウントする WSL ディストリビューションの名前です。ファイルシステムを同時に複数の Ubuntu へマウントすると破損が怖いので、マウント先のディストリビューションを指定することにしました。調べてみると ext4 は Multiple Mount Protection という保護機能があり、同時に複数からマウントした際の破損を軽減できるそうですが、今回はその機能を使用せず、マウント先を 1 つに限定しました。

異なるディストリビューションでマウントしたくなったら、このテキストファイルの中身をマウントしたいディストリビューション名に更新し、Ubuntu を再起動します。再起動の順序は、まずは今マウントしている Ubuntu を、次に新たにマウントする Ubuntu を再起動します。逆にすると一時的に 2 つの Ubuntu からマウントした状態になるため、危険かもしれません。

"$PS" Start-Process -FilePath wsl.exe -Verb RunAs ...wsl.exe を管理者権限で実行するための記述です。-Verb RunAs が管理者権限で起動するためのオプションです。この行が実行されようとすると、画面に権限昇格のダイアログ(UAC ダイアログ)が出てきます。

wsl.exe --mount の後、ちょっとだけ待たないと Ubuntu 側で認識されないようなので、最大 5 秒ほど待つ仕組みにしています。

ファイルを作ったら実行権限を与えておきます。

$ sudo chown +x /usr/local/bin/mount_uchan_home.sh

Systemd の有効化

Systemd を用いて上記のスクリプトを Ubuntu 起動時に実行させます。(もちろん Systemd じゃない仕組みでやっても良いですが、root ではないユーザ権限で自動起動されてしまうと、root が必要な mount コマンドが失敗しますから、root で実行される仕組みである必要があります。)

WSL2 の Ubuntu 20.04 はデフォルトで Systemd が無効なので、上記の自動マウントの仕組みを動作させるには wsl.conf を編集して Systemd を有効化する必要があります。/etc/wsl.conf に次のような設定を追加します。

[boot]
systemd = true

これで Ubuntu を再起動させれば Systemd が有効になります。

ちなみに、WSL2 上の OS の再起動は、cmd.exe 等で wsl.exe --shutdownwsl.exe --terminate ディストリビューション名 で終了させ、再度 WSL ターミナルを開けば OK です。

Systemd のユニットファイルを作成

/etc/systemd/system に次のような内容のファイルを作ります。ファイル名は何でも良いですが、私は mount-uchan-home.service という名前にしました。

[Unit]
Description=Mount VHDX image for uchan home
After=local-fs.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/mount_uchan_home.sh

[Install]
WantedBy=multi-user.target

ExecStart に先ほど作成したスクリプトのパスを指定します。

このタスクが自動的に実行されるよう、enable します。

$ sudo systemctl enable mount-uchan-home.service
Created symlink /etc/systemd/system/multi-user.target.wants/mount-uchan-home.service → /etc/systemd/system/mount-uchan-home.service.

この状態で Ubuntu を再起動すれば自動マウントされるはずです。自動マウントがうまくいかない場合、sudo /usr/local/bin/mount_uchan_home.sh として手動でスクリプトを実行してうまくマウントされるかを確認したり、journalctl -u mount-uchan-home でログを確認したりして原因を探ります。

上記のユニットファイルの記述では、指定したスクリプトが実行され終わったらサービスが終了したと判定され、inactive 状態となります。記述をこだわるとしたら、ディスクイメージがマウントされている間はずっと active になり、systemctl stop でマウントを解除できるような仕組みにするのが理想だと思います。今回の筆者の需要ではホームディレクトリをマウントするというものだったので、途中でマウント解除することは想定しずらく、そこまで手の込んだ記述をしませんでした。



この記事を参照しているブログ内記事はありません。

作成:2025-10-11 14:59:33

最終更新:2025-10-11 14:59:33