サイトロゴ
Docker Created: 2026/05/31 Updated: 2026/06/02

WindowsのDockerビルドが遅い原因はWSL外のファイル配置だった|8分→3分台に改善した話

同じプロジェクトなのにMacより遅いWindowsのDockerビルド。原因はCPUやメモリではなく、Windows側ファイルシステム(/mnt/c)の配置でした。WSL2の/home配下へ移すだけでビルドが8分から3分台に。理由と具体的な設定手順を初心者向けに解説します。

はじめに:Windowsだけビルドが遅い

同じSvelteKitプロジェクトをビルドしているのに、MacBook Air M1では3分台、WindowsのDocker環境では8分程度かかっていました。

Windows機のスペックは、AMD Ryzen 9 8945HS、メモリ96GB。MacBook Air M1はメモリ16GBです。単純なスペックだけを見るとWindowsのほうが圧倒的に強そうですが、実際にはMacのほうがかなり速いという結果になりました。

Before

約8分

Windows Docker

Windows側ファイルシステムでビルド

After

3分台

Windows + WSL2

/home配下へ移動してビルド

Reference

約3分

MacBook Air M1

メモリ16GB

🎯 結論

原因はCPUやメモリ不足ではなく、 Windows側ファイルシステム上のプロジェクトをDockerコンテナへマウントしていたこと でした。WSL2のLinuxファイルシステム、つまり/home/ユーザー名 配下へプロジェクトを移すことで、ビルド時間がMacに近い3分台まで改善しました。

この記事では、コマンドプロンプト・PowerShell・WSLの違いから、 そもそもなぜWindows Dockerでビルドが遅くなりやすいのか (ここが一番のポイントです)、そしてWSL2を使ったDocker開発環境の作り方までを、順を追って整理します。


今回の環境

まず、今回比較した環境です。単純なマシンスペックだけで見ると、Windows機のほうがかなり余裕があります。

環境 スペック ビルド時間 補足
Mac MacBook Air M1 / Memory 16GB 約3分 比較用
Windows(変更前) Windows 11 / AMD Ryzen 9 8945HS / Memory 96GB 約8分 Windows側のプロジェクトをDockerでビルド
Windows(変更後) 同じWindows機 + WSL2 Ubuntu 3分台/home 配下のプロジェクトをDockerでビルド

対象プロジェクトは、SvelteKit / Vite / Tailwind CSS v4 / TypeScript / Bun / Docker Compose を使った構成です。Vite や Tailwind CSS のビルドは「細かいファイルを大量に読み書きする」処理が多いため、ファイルシステムの差がビルド時間にそのまま出やすい構成でした。後述しますが、この「大量の細かいファイルアクセス」が今回のボトルネックの正体です。


前提知識:コマンドプロンプト・PowerShell・WSLの違い

Windowsで開発環境を作ると、コマンドプロンプト、PowerShell、WSLという似たような入口が出てきます。「なぜ遅いのか」を理解する土台になるので、まずはそれぞれの役割を整理します。

コマンドプロンプト

昔からあるWindows標準のコマンド実行環境です。cmd.exe と呼ばれ、dir copy などWindows系のコマンドを扱います。

PowerShell

Windows向けの高機能なシェルです。管理者作業やスクリプトに強く、wsl --install wsl --set-default などのWSL操作でもよく使います。

WSL

Windows上でLinux環境を動かす仕組みです。UbuntuなどをWindows内で動かし、Linuxのファイルシステム、bash、apt、git、dockerコマンドを扱えます。

✨ Docker開発ではWSL側を使う

Dockerコンテナは基本的にLinux環境で動きます。そのためWeb開発では、PowerShellやコマンドプロンプトからWindows側のファイルを扱うより、WSLのUbuntu内でプロジェクトを扱ったほうが相性がよくなります。なぜそうなるのかは、次のセクションで掘り下げます。


Windows Dockerでビルドが遅くなりやすい理由

変更前は、Windows側のファイルシステム(C:\Users\... )にプロジェクトを置いた状態でDockerビルドをしていました。図にすると、こういう流れです。

遅くなりやすい構成

Windows側ファイルシステムをDockerへマウントする流れ

Before

C:\Users\...\project

⇢遅⇢

bind mount

Dockerコンテナ

bun run build

コンテナがファイルを読むたびに、Windows側ファイルシステムとの「境界」をまたぎます。

なぜ「ファイルシステムをまたぐ」と遅いのか

ここが今回の核心です。少し仕組みの話になりますが、初心者の方こそ押さえておくと、似た問題に出会ったときに自分で原因を切り分けられるようになります。

WSL2は、実は Windows上で動く軽量な仮想マシン(VM) で、本物のLinuxカーネルと独自のLinuxファイルシステム(ext4)を持っています。そしてWSL2から見えるファイルの置き場所には、性能がまったく違う2種類があります。

🟢 Linux側ファイルシステム(速い)

/home/ユーザー名/... 配下。WSL2というVMが自分のディスク(ext4)として直接持っている領域です。Linuxのアプリ(Dockerコンテナ含む)から見ると、地続きの「自宅」のような場所で、ファイルアクセスのオーバーヘッドがほぼありません。

▲ ここに「境界」があり、またぐたびに変換コストがかかる ▼

🔴 Windows側ファイルシステム(遅い)

/mnt/c/... (=WindowsのC:\ )配下。WSLからは見えますが、実体はVMの外にあるWindowsのディスクです。アクセスのたびに、VMの境界を越えてWindowsへ問い合わせる仕組み(9Pというプロトコル)を経由します。

たとえるなら、Linux側は「同じ部屋にある本棚」、Windows側は「隣の建物にいる司書さんに毎回取り寄せを依頼する書庫」のようなものです。1冊や2冊なら違いは気になりません。ところが、 何万回もファイルを開け閉めする処理 になると、1回ごとの取り寄せコストが積み重なって、数分単位の差になります。

ビルドは「小さなファイル」を大量に触る処理

そして、まさにフロントエンドのビルドがこの弱点を突きます。SvelteKit / Vite / Tailwind CSS / TypeScript のビルドでは、次のような大量の細かいファイルアクセスが発生します。

node_modules

依存パッケージが数千〜数万ファイル単位で展開され、ビルド時に大量に読み込まれます。

Vite の依存解決

import を辿りながら多数のモジュールを開き、.vite キャッシュへ細かく書き出します。

Tailwind のクラス検出

ソース全体をスキャンして使われているクラスを抽出するため、ファイル走査が広範囲に及びます。

.svelte-kit / build 出力

中間生成物や成果物を細かく書き出し、その後また読み直す場面も多くあります。

これらの「1回1回は軽いが、回数が膨大な」アクセスすべてに境界をまたぐコストが乗ると、合計で大きな差になります。これが、変更前に約8分かかっていた正体です。

⚠️ ️ CPUやメモリだけでは判断できない

Ryzen 9 + メモリ96GBでも、DockerコンテナからWindows側ファイルシステムを大量に読む構成では、CPUやメモリではなく ファイルI/O(しかも境界をまたぐI/O) がボトルネックになります。今回のように「高スペックWindowsよりMacBook Airのほうが速い」という逆転現象も、これで説明がつきます。

Docker公式のWSL2ベストプラクティスでも、bind mountのファイルI/O性能を最大化するには、ソースコードやbind mount対象のデータをLinuxコンテナ側、つまり WSL2のLinuxファイルシステム側に置くこと が明確に推奨されています。今回の改善は、まさにこの推奨に沿った対応です。


改善のポイント:WSLの /home 配下にプロジェクトを置く

WSLを使うだけでは不十分 です。重要なのは、プロジェクトの実体を/home/ユーザー名 配下、つまり前述のLinux側ファイルシステムに置くことです。

改善後の構成

After

ex) /home/[user_name]/projects/[project_name]

bind mount

Dockerコンテナ

bun run build

Linuxファイルシステム上のプロジェクトを、同じLinux内のコンテナから扱うため、境界をまたがず高速です。

まず30秒でできる診断:いま自分はどこにいるか

「WSLのターミナルを開いている=速い」ではありません。判断のカギは、開いているターミナルではなく プロジェクトの実体がどこにあるか です。まずはpwd で現在地を確認しましょう。

bash
$ pwd

# 🔴 これだと実体はWindows側(遅い)
/mnt/c/Users/[user_name]/projects/[project_name]

# 🟢 これならLinux側(速い)
/home/[user_name]/projects/[project_name]
配置場所 評価 理由
/home/[user_name]/projects/[project_name] おすすめ WSLのLinuxファイルシステム上にあり、境界をまたがない
/mnt/c/Users/... 避けたい WSLから見えていても実体はWindows側ファイルシステム
C:\Users\...\project 避けたい Dockerコンテナから大量アクセスすると遅くなりやすい

✨ 「WSLを使っているか」より「どこに置いているか」

WSLのターミナルを開いていても、pwd の結果が/mnt/c/... なら、まだWindows側ファイルシステム上で作業しています。/home/... 配下に置き直すのがポイントです。


WSL2の導入と基本設定

WSLをまだ使っていない場合は、PowerShellを管理者で開いてインストールします。

Step 1:WSLをインストール

bash
PS> wsl --install

ディストリビューションを明示してインストールする場合は、以下のように実行します(既定でもUbuntuが入ります)。

bash
PS> wsl --install -d Ubuntu

✨ インストール後は再起動を

初回インストール時はWindowsの再起動を求められることがあります。再起動後にUbuntuが自動で立ち上がり、Linux側のユーザー名とパスワードの初期設定を求められます。

Step 2:ディストリビューションを確認

bash
PS> wsl -l -v
  NAME      STATE           VERSION
* Ubuntu    Stopped         2

VERSION 2 になっていることを確認してください。1 の場合はwsl --set-version Ubuntu 2 で2に変換できます(WSL2でこそファイルI/Oの恩恵が得られます)。

Step 3:デフォルトをUbuntuにする

bash
PS> wsl --set-default Ubuntu

WSLを完全停止したい場合は、以下を使います(設定変更後の反映や、調子が悪いときの再起動に便利です)。

bash
PS> wsl --shutdown

Ubuntuへログインしてホームディレクトリを確認する

Ubuntuへログインしたら、まずユーザー名と現在位置を確認します。

bash
$ whoami
<user_name>

$ cd ~
$ pwd
/home/<user_name>

ここが/home/<user_name> になっていればOKです。この配下にプロジェクトを置きます。

bash
$ cd ~
$ mkdir -p projects
$ cd projects

✨ Windows側からも見える

WSL内のファイルは、Windowsのエクスプローラーから\\wsl.localhost\Ubuntu\home\<user_name>\projects のように参照できます(従来の\\wsl$\Ubuntu\... 表記も使えます)。ただし、編集や開発作業はこのパスを直接いじるのではなく、VSCodeのRemote WSLから行うのがおすすめです。


Docker DesktopのWSL連携を有効にする

Docker Desktopを使う場合は、WSL2 backendとWSL Integrationを有効にします。

Docker Desktop側で確認する設定

  • ✔️ Docker Desktopを起動する
  • ✔️ Settings → General → Use the WSL 2 based engine を有効にする
  • ✔️ Settings → Resources → WSL Integration → Ubuntu を有効にする
  • ✔️ Apply & Restartで反映する

WSL内で以下を実行し、Dockerコマンドが使えることを確認します。

bash
$ docker version
$ docker compose version

WSL内にプロジェクトを配置する

Gitリポジトリから取得する場合は、 WSL内の~/projects 配下で cloneします。Windows側でcloneしてWSLから参照する、という遠回りは避けてください。

bash
$ cd ~/projects
$ git clone <repository-url> <project_name>
$ cd <project_name>

✨ cloneはWSL内で。改行コードのトラブルも防げる

WSL内でcloneしておくと、速度面だけでなく改行コード(CRLF/LF)の混乱も避けやすくなります。Windows側のGitでcloneするとファイルがCRLFに変換され、Linuxコンテナ内のシェルスクリプトが動かない、といった地味なハマりが起きがちです。

VSCodeで開く場合は、WSL内のプロジェクトディレクトリからcode . を実行します。

bash
$ cd ~/projects/<project_name>
$ code .

✨ Remote WSLで開く

VSCode左下にWSL: Ubuntu のような表示が出ていれば、WSL上のプロジェクトを開けています。Windows側からC:\... のフォルダとして開くのではなく、WSL側から開くのがポイントです(VSCode拡張「WSL」が必要です)。


Docker Composeで開発環境を起動する

プロジェクトディレクトリにdocker-compose.yml がある場合は、そのディレクトリでDocker Composeを実行します。

bash
$ cd ~/projects/<project_name>
$ docker compose up -d --build

ログを見る場合は以下です。

bash
$ docker compose logs -f

コンテナに入る場合は、サービス名を指定します。

bash
$ docker compose exec app bash

# bashがないイメージの場合
$ docker compose exec app sh

docker-compose.ymlのvolume設定で注意すること

開発時はソースコードをコンテナへbind mountします。ただし、node_modules .svelte-kit のようにファイル数が多いディレクトリは、 named volumeに逃がす と安定しやすくなります。ホスト側のファイルでコンテナ内の生成物を上書きしてしまう事故も防げます。

yaml
services:
  app:
    build:
      context: .
    working_dir: /app/<project_name>
    volumes:
      - .:/app/<project_name>
      - node_modules:/app/<project_name>/node_modules      # ← named volumeへ
      - sveltekit_cache:/app/<project_name>/.svelte-kit    # ← named volumeへ
    ports:
      - "5160:5160"
    command: bun run dev

volumes:
  node_modules:
  sveltekit_cache:

✨ ポイント

.:/app/<project_name> は、WSL上の/home/<user_name>/projects/<project_name> をコンテナへマウントします。Windows側のC:\... ではないため、ここまで整えてはじめてファイルI/Oのボトルネックを避けられます。

SvelteKit / Viteでブラウザからアクセスする場合は、dev serverを コンテナの外から見えるように--host 0.0.0.0 を付けます。さらに、上のdocker-compose.yml で公開しているポート(ここでは5160 )と一致するよう--port も明示しておくと、ポートのズレによる「つながらない」を防げます。

text
{
  "scripts": {
    "dev": "vite dev --host 0.0.0.0 --port 5160 --mode dev"
  }
}

💡 ポート番号は3か所をそろえる

「Composeのports 5160:5160 )」「Viteの--port 」「ブラウザでアクセスするURL」の3か所を同じ番号にそろえます。Viteは既定だと5173 で起動するため、--port を省略するとComposeの公開ポートと食い違ってアクセスできなくなります。

Windows側のブラウザからは、通常どおり以下でアクセスできます。

text
http://localhost:5160

ステージング・本番ビルドはDockerfile内で完結させる

開発中はbind mountでホットリロードを使うのが便利です。一方、ステージング・本番用のビルド資材を作るときは、Dockerfileのbuilder stageでCOPY . . してビルドする構成に寄せると、環境差分を減らしやすくなります。bind mountに依存しない分、CI/CDサーバーなど手元と違う環境でも結果がブレにくくなります。

用途 おすすめ構成 理由
開発 bind mount +bun run dev ソース変更を即反映しやすい
ステージング・本番ビルド Dockerfile内でCOPY . . → build 再現性が高く、ホスト依存を減らしやすい
text
FROM oven/bun:latest AS builder

WORKDIR /app/<project_name>

# 依存だけ先にコピー → install をキャッシュに乗せる
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile

# 残りのソースをコピーしてビルド
COPY . .
RUN bun run build:staging

✨ 依存のコピーを先にする理由

package.json bun.lock を先にコピーしてからbun install すると、ソースだけ変更したビルドではinstall 層がキャッシュされ、再ビルドが速くなります。bun.lock はBun 1.2以降で標準になったテキスト形式のロックファイルです(古いbun.lockb を使っている場合はそちらを指定してください)。


よくあるハマりポイント

❓ WSLを使っているのに速くならない

まずpwd を確認します。/mnt/c/Users/... なら、WSLから見えているだけで実体はWindows側ファイルシステムです。/home/ユーザー名/projects/... 配下へプロジェクトを移してください。コピーではなく、WSL内でgit clone し直すのが確実です。

bash
$ pwd
/home/<user_name>/projects/<project_name>

❓ wsl -u ユーザー名 でユーザーが見つからない

Windowsのユーザー名とWSL内のLinuxユーザー名は別物です。rootでログインして/etc/passwd を確認し、必要ならadduser で作成します。

bash
$ grep <user_name> /etc/passwd

# 存在しない場合(rootで実行)
# adduser <user_name>
# usermod -aG sudo <user_name>

❓ systemd user session の警告が出る

ログインできていて、whoami pwd が正常なら、開発作業自体は続けられる場合があります。気になる場合は/etc/wsl.conf [boot] systemd=true 設定や、WSLの更新状況を確認します。

bash
$ whoami
$ pwd
$ ps -p 1 -o comm=
systemd   ← systemd が PID 1 なら有効

❓ localhostでアクセスできない

Docker Composeのポート公開、SvelteKit/Viteの--host 0.0.0.0 、そして--port がComposeの公開ポートと一致しているかを確認します。コンテナ側だけでlocalhost に閉じている(--host がない)と、Windows側ブラウザからアクセスできません。


まとめ

この記事で学んだこと

  • ✔️ WindowsのDockerビルドが遅い場合、まずCPUやメモリよりファイル配置を疑う
  • ✔️ WSL2は軽量VMで、/home (Linux側・速い)と/mnt/c (Windows側・遅い)で性能が大きく違う
  • ✔️ 遅さの正体は、境界をまたぐファイルI/O。ビルドは小さなファイルを大量に触るため差が出やすい
  • ✔️ プロジェクトは/home/ユーザー名/projects 配下に置き、WSL内でgit clone する
  • ✔️ Docker ComposeはWSL内のプロジェクトディレクトリから実行する
  • ✔️ VSCodeはcode . でRemote WSLとして開く
  • ✔️node_modules .svelte-kit はnamed volumeに逃がすと安定しやすい
  • ✔️ Compose・Vite・ブラウザURLのポート番号は3か所そろえる
  • ✔️ ステージング・本番ビルドはDockerfile内で完結させると再現性を高めやすい

🚀

「WSLを使う」だけでなく「/home配下に置く」ことが本質

今回の改善で、Windows環境でもMacBook Air M1に近い3分台でビルドできるようになりました。 WindowsでDockerビルドが遅いと感じたら、Dockerfileやマシンスペックを疑う前に、まずpwd でプロジェクトの配置場所を確認するのがおすすめです。

Windows側は遅くなりやすい

/mnt/c は避ける

/home 配下へ配置

Remote WSLで開く

Docker ComposeはWSL内から