
この記事は every Tech Blog Advent Calendar 2025 の 14 日目の記事です。
はじめに
こんにちは。デリッシュキッチン開発部でバックエンドエンジニアをしている鈴木です。
Docker を使ってローカル環境で開発をしている方なら、かつて macOS 上の Docker Desktop でコンテナ内のファイルアクセスが非常に遅いという問題に悩まされた経験があるかもしれません。ホットリロード付きの開発サーバーがファイル変更に反応するのが遅かったり、テストスイートやビルドに時間がかかったりするケースです。
この問題の背景には、開発の利便性とパフォーマンスのトレードオフが存在していました。ホスト上のコードをコンテナに共有すれば編集が簡単ですが、macOS 特有のアーキテクチャ1により性能が低下します。一方、コンテナ内部にファイルを置けば高速ですが、ホストから直接編集できず開発体験が損なわれます。
本記事では、なぜ macOS でホスト上のコードをコンテナに共有する際に遅延が発生するのかをその仕組みから解説し、Docker Desktop が開発の利便性とパフォーマンスのトレードオフにどう対処してきたか、そして最新の技術 (VirtioFS, Synchronized File Shares) によってどのように両立が可能になったかを紹介します。
1. なぜファイル共有の仕組みが必要なのか
Docker コンテナは通常、ホスト OS と分離された独立のファイルシステムを持ちます [1]。開発を行う際には、ホスト上のディレクトリをコンテナ内に共有する仕組みが必要になります。
Bind マウントと Named Volume
Docker には、ホストとコンテナ間でファイルを共有する主な方法が2つあります。
Bind マウントは、ホスト上の特定ディレクトリ (例: /Users/suzuki/myapp) をコンテナ内のパス (例: /app) に直接共有する機能です [2]。ホストのエディタでファイルを編集すると、その変更がすぐにコンテナに反映されます。この利便性から、開発中のソースコードの共有に最適です。
Named Volume は、Docker 自身が管理するストレージ領域です [3]。ホストのファイルシステムから独立しており、Docker が内部で管理します。永続化が必要なデータベースファイルや、頻繁に変更しない依存ライブラリの保存に適しています。
以下の表は、両者の特性を比較したものです [2], [3]。
| 観点 | Bind マウント | Named Volume |
|---|---|---|
| データの実体 | ホスト OS 上のディレクトリ | Docker 管理領域 |
| 性能 (macOS) | 遅い (ホスト OS - VM 間の通信が発生) | 速い (VM 内で完結) |
| ホストからの編集 | 可能 (リアルタイム反映) | 困難 (docker cp で取り出す必要) |
| ユースケース | 開発中のソースコード | 永続化が必要なDBや依存ライブラリ |
開発ワークフローでは、ホスト上のエディタでコードを書き、それをコンテナ内で即座に実行できることが重要です。そのため、性能面での課題はありますが、本記事では Bind マウントに焦点を当てます。
macOS特有の問題
ここで重要なポイントがあります。Mac 版 Docker Desktop では、Linux コンテナがホスト OS 上で直接動作していないという点です [4], [5]。
Docker Desktop は内部で軽量の Linux 仮想マシン (VM) を動かしており、コンテナはこの VM 上で動作しています [4]。そのため、ホストの macOS ファイルシステムと VM 内部の Linux コンテナとの間でファイル共有のための仲介が必要になります [4]。
Linux ネイティブ環境との違い
- Linux ホスト上: コンテナはホストカーネルを共有しており、Bind マウントしてもオーバーヘッドなくカーネル経由で直接ホスト FS にアクセスできる [6]
- Docker Desktop (macOS): ホスト OS - VM 間の通信が発生し、これが遅さの原因となる

このように、ファイル共有の仕組みはホストとコンテナ間でファイルをやり取りするために必須ですが、Docker Desktop (macOS) では VM 層が存在するため、ファイルアクセスのたびにホスト OS - VM 間の通信が発生します。この通信の積み重ねが、性能低下の根本原因となっています [7]。
2. Docker Desktop のファイル共有の仕組み
では、ホスト OS - VM 間の通信を発生させているファイル共有の仕組みとは、具体的にどのようなものなのでしょうか。
Docker Desktop のファイル共有は、ホスト OS と VM 間でファイルシステムを中継する仮想ドライバによって実現されています [8]。
この仕組みは以下の主要なコンポーネントから構成されています。
構成要素
ホスト側ファイルサーバー (osxfs / gRPC-FUSE / virtiofsd など)
- ホスト(macOS)上で動くプロセス [8]
- 共有対象ディレクトリの実際のファイル操作を担当する
VM 側ドライバ (FUSE クライアント / VirtioFS ドライバ)
- Linux VM 内で動作するファイルシステムドライバ [8]
- コンテナからのファイルIO要求を受け付ける
通信チャネル (vsock/Hypervisor 共有メモリ 等)
- ホストと VM 間でデータをやり取りするためのチャネル

これらのコンポーネントがどのように連携するかを見てみましょう。
コンテナ内のファイル操作は、VM 内のドライバが受け取り、ホスト OS 上の対応するファイルに処理を渡し、結果をコンテナに返すというリモートプロシージャーコール的な処理が行われます [8]。この一連の流れが、すべてのファイルアクセスで繰り返されることが、性能問題の直接的な原因となります。
3. 性能問題の実例と実際に問題になるケース
この仕組みが実際の開発現場でどのような問題を引き起こすのか、具体例を通して見ていきましょう。
Node.js開発環境での問題
大量の小さなサイズのファイルを含む Node.js プロジェクトをコンテナで動かすケースで、問題を具体的に見てみましょう。
シナリオ
あなたは Mac で React アプリケーションの開発をしています。ソースコードはホスト上にあり、docker run -v ~/myapp:/usr/src/app ... のように Bind マウントしてコンテナに共有しています。
コンテナ内で npm install を実行すると、node_modules に大量のパッケージ (数万ファイル) がインストールされます。続いて開発サーバーを起動すると、依存関係を解析するために node_modules 以下のファイルを再帰的に読み取ります。
何が起きているか
コンテナ内プロセスが発行する大量のシステムコール (open, read, stat, readdir など) が、一つ一つホストとの間を往復します。

この往復を依存ファイル数だけ繰り返すため、以下の問題が発生します。
- ホスト OS - VM 間の通信遅延が積み重なり、ファイル数に比例して処理時間が増大 [7]
- メタデータ操作 (stat, readdir) が特にボトルネックになる [7]
- 結果: ある空の React アプリ (約 37k の小ファイル) でのベンチマークでは、Linux ネイティブ環境に比べて約 3.5 倍の時間がかかり、従来の gRPC-FUSE 実装では最大 10 倍以上遅くなるケースも確認されている [9]
Docker 公式も「モダンな開発ツール (コンパイラやパッケージマネージャ) は何千もの readdir(), stat(), open() を発行する。仮想ファイルシステムではその一つ一つがホストとVMの間を渡らなければならない」と指摘しています [7]。
実際に問題になるケース
上記の Node.js 例に限らず、以下のような状況で、Bind マウントの性能問題が実際に表面化します。
- 依存ファイルが多いプロジェクトのビルド/起動: Node.js の node_modules 、Java の Maven 、Python の venv など [7]
- ホットリロードやファイル監視: 対象ディレクトリ全体を監視するために繰り返される stat 操作
- 自動テストの実行: テストごとに多数のモジュールをインポートする操作
- パッケージの依存関係解決・インストール:
npm installやcomposer installなど
ポイント: 小規模なアプリや数個の大きなファイルを扱う程度であれば、体感できる差は出にくいです。問題は、大量のファイルに対して頻繁にアクセスするケースに集中します [7]。
4. 技術の進化による解決策
第 3 章で見たように、Bind マウントには性能問題がありました。しかし、ホストで快適に編集できるという利便性は開発には不可欠です。このトレードオフに対し、Docker Desktop はどのように対処してきたのでしょうか。実は、段階的な技術改善により、このトレードオフの緩和、そして最終的には解決が実現されてきました。
技術の進化
- Legacy osxfs (初期) : Docker for Mac の独自実装。大量ファイルで遅いという問題があった [9]
- gRPC-FUSE (2020~2022年頃) : プロトコル効率化を図ったが、依然としてボトルネックは残った [9]
- VirtioFS (近年) : Docker Desktop 4.x で macOS 12.5 以降でデフォルト採用。ほとんどの開発者に十分な性能を提供 [7], [10]
- Synchronized File Shares (同期共有) (Docker Desktop 4.27+) : 大規模プロジェクト向けに、利便性と性能の完全な両立を実現 [10]
この中で、現在利用可能な主要な2つの技術 (VirtioFS と Synchronized File Shares) について、詳しく見ていきましょう。
VirtioFS: ほとんどの開発者に十分な解決策
従来の osxfs や gRPC-FUSE では、ホスト OS - VM 間の通信の実装が非効率でした。各ファイル操作がネットワークプロトコルに似た方式で処理され、大きなオーバーヘッドが発生していました。
VirtioFS は、この通信経路を根本から見直します。仮想化技術の標準規格である virtio に基づいた専用の高速通信チャネルを使用し、仮想マシンとホストが同じ物理マシン上にあるという事実を活かした最適化を実現します [11]。依然として Bind マウントでありホスト OS - VM 間の通信は発生しますが、通信プロトコルとデータパスが大幅に効率化されることで、劇的な性能向上を達成しています。

この改善により、ファイルシステム操作の時間が従来の Bind マウントと比較して最大 98% 短縮されました [7]。実際のベンチマークでは、ある PHP プロジェクトで約 4 倍の速度向上 (93.7 s → 25.5 s) が確認されています [12]。Docker 公式も「ほとんどの開発者とプロジェクトにとって優れた初期設定の解決策」と評価しており [10]、VirtioFS はほとんどの開発者にとって十分な性能を提供します。ただし、特に大規模なプロジェクトでは、さらなるパフォーマンスが必要になる場合があります [10]。
Synchronized File Shares (同期共有) : 大規模プロジェクト向けの完全な解決策
VirtioFS はほとんどのケースで十分ですが、数千~数百万のファイルを含む大規模プロジェクト (100,000 ファイル以上の大規模リポジトリや、数百 MB ~ 数 GB の総容量のプロジェクト) では、追加のパフォーマンスが必要になります [10]。
VirtioFS で通信効率は大幅に改善されましたが、大規模プロジェクトでは依然としてホスト OS - VM 間の通信そのものがボトルネックになります。数万回、数十万回のファイル操作が発生する場合、いくら通信を効率化しても、その回数の多さが性能に影響するのです。
Synchronized File Shares は、この問題に対して根本的に異なるアプローチを取ります。コンテナのファイルアクセスとホスト OS - VM 間の通信を完全に分離するのです [10]。具体的には、VM 内に ext4 形式のキャッシュ領域を作成し、ホストのファイルのコピーをここに保持します。コンテナはこの VM 内キャッシュに直接アクセスするため、すべてのファイルシステムコール (readdir(), stat(), open()/read()/write()/close()) が Linux カーネルで直接処理され、ホスト OS - VM 間の通信が発生しません。一方、オープンソースのファイル同期技術である Mutagen [13] がバックグラウンドでホストとキャッシュを超低遅延で双方向同期します [10]。コンテナからは VM 内ローカルアクセスとなり、ネイティブ Linux 並みの性能を実現しつつ、ホストでの編集も自動的にコンテナに反映されます。

その結果、従来の Bind マウントと比較して 2 ~ 10 倍の速度向上が実現されました [10]。
ただし、ファイルを二重に保持するためディスク容量を余分に消費します [10]。また、同期は非同期で行われるため、ホストで編集してからコンテナに反映されるまで若干のラグが発生する可能性がありますが、通常の開発では問題にならない程度です。なお、この機能は有料アカウント (Docker Pro、Team、または Business) が必要です [10]。
Synchronized File Shares (同期共有) は、大規模プロジェクトにおいて利便性と性能の両立を実現します。VirtioFS で十分な性能が得られない場合の解決策となります。
まとめ
macOS 上の Docker Desktop では、コンテナが VM 上で動作するというアーキテクチャ上の制約により、ファイル共有に特有の性能問題が発生します。開発にはホストで編集できる Bind マウントが適していますが、macOS ではホスト OS - VM 間の通信が頻繁に発生し、大量の小さなファイル操作が必要な開発環境では、この通信コストが積み重なって性能が大幅に低下します。
この問題の背景には、利便性とパフォーマンスのトレードオフが存在していました。Bind マウントはホストから直接編集できるため開発体験が優れていますが、ホスト OS - VM 間の通信により性能が犠牲になります。一方、Named Volume は VM 内部で完結するため高速ですが、ホストから直接編集できないという不便さがありました。
Docker Desktop はこの問題に対し、段階的に解決策を提供してきました。VirtioFS は、ホスト OS - VM 間の通信の効率を大幅に改善し、ほとんどの開発者にとって十分な性能を実現しました。さらに、Synchronized File Shares (同期共有) は、VM 内に ext4 キャッシュを配置してコンテナのアクセスを高速化しつつ、バックグラウンドでホストと同期することで、大規模プロジェクトにおいても利便性と性能の両立を可能にしました。
このアーキテクチャの変更により、利便性と性能のトレードオフが解消され、開発者はホストでの快適な編集とネイティブ Linux 並みの性能を同時に享受できるようになりました。
Appendix: FUSE による追加のオーバーヘッド
本文ではホスト OS - VM 間の通信を主なボトルネックとして説明しましたが、初期の Docker Desktop 実装 (osxfs/gRPC-FUSE) では、さらに FUSE によるオーバーヘッドも存在していました。
FUSEとは
FUSE (Filesystem in Userspace) は、ファイルシステムの処理をユーザ空間のプロセスで実装できる仕組みです [14]。通常のファイルシステム (ext4 など) はカーネル内で処理が完結するため高速ですが、FUSE では追加の処理が発生します。コンテナ内のアプリケーションがファイル操作を行うと、その要求はまずカーネル内の FUSE モジュールに届きます。FUSE モジュールはこれをユーザ空間のファイルサーバープロセスに転送し、処理結果を再びカーネル経由でアプリケーションに返します。

このカーネル空間とユーザ空間の間のコンテキストスイッチが、ホスト OS - VM 間の通信に加えて追加のオーバーヘッドとなっていました [7]。
VirtioFSによる改善
VirtioFS は、より効率的な通信パスを使うことで、この FUSE スタックのオーバーヘッドを大幅に削減しています。これが、VirtioFS で 4 倍程度の性能向上が実現された理由の一つです。
参考文献
[1] Docker Inc., "Manage data in Docker," Docker Documentation. [Online]. Available: https://docs.docker.com/storage/
[2] Docker Inc., "Use bind mounts," Docker Documentation. [Online]. Available: https://docs.docker.com/storage/bind-mounts/
[3] Docker Inc., "Use volumes," Docker Documentation. [Online]. Available: https://docs.docker.com/storage/volumes/
[4] Docker Inc., "Docker Desktop for Mac," Docker Documentation. [Online]. Available: https://docs.docker.com/desktop/mac/
[5] Docker Inc., "Docker Desktop for Windows," Docker Documentation. [Online]. Available: https://docs.docker.com/desktop/windows/
[6] Docker Inc., "Docker storage drivers," Docker Documentation. [Online]. Available: https://docs.docker.com/storage/storagedriver/
[7] Docker Inc., "Speed boost achievement unlocked on Docker Desktop 4.6 for Mac," Docker Blog, Mar. 2022. [Online]. Available: https://www.docker.com/blog/speed-boost-achievement-unlocked-on-docker-desktop-4-6-for-mac/
[8] Docker Inc., "Change your Docker Desktop settings," Docker Documentation. [Online]. Available: https://docs.docker.com/desktop/settings-and-maintenance/settings/
[9] P. Mainardi, "Docker on MacOS is slow and how to fix it," CNCF Blog, Feb. 2023. [Online]. Available: https://www.cncf.io/blog/2023/02/02/docker-on-macos-is-slow-and-how-to-fix-it/
[10] Docker Inc., "Announcing Docker Desktop 4.27: Speed Boost with Synchronized File Shares," Docker Blog, Feb. 2024. [Online]. Available: https://www.docker.com/blog/announcing-synchronized-file-shares/
[11] S. Hajnoczi et al., "Virtio-fs: A shared file system for virtual machines," virtio-fs Project. [Online]. Available: https://virtio-fs.gitlab.io/
[12] J. Geerling, "New Docker for Mac VirtioFS file sync is 4x faster," Jeff Geerling's Blog, Mar. 2022. [Online]. Available: https://www.jeffgeerling.com/blog/2022/new-docker-mac-virtiofs-file-sync-4x-faster
[13] Docker Inc., "Mutagen joins Docker," Docker Blog, Jan. 2022. [Online]. Available: https://www.docker.com/blog/mutagen-acquisition/
[14] "Filesystem in Userspace," Linux Kernel Documentation. [Online]. Available: https://www.kernel.org/doc/html/next/filesystems/fuse.html
- この問題は Windows (WSL2) でも同様に発生します。基本的な仕組みと解決策は Windows にも適用できます。↩