Docker の Volume と Bind Mount の違いを整理する
概要
Docker でデータを永続化・共有する方法として、よく登場するのが Volume(ボリューム) と Bind Mount(バインドマウント) です。どちらも「コンテナの外にデータを置く」仕組みですが、管理の主体・パスの指定方法・ユースケースが明確に異なります。この記事では両者の違いを仕組みから整理し、どちらを選ぶべきかを判断できるようにします。内容はすべて Docker の公開ドキュメントに基づく一般的な知見です。
なぜデータ永続化が必要か
Docker コンテナのファイルシステムは、コンテナを削除すると一緒に消えます。コンテナ内に書き込んだデータは「書き込み可能レイヤー」に乗りますが、これはコンテナのライフサイクルに縛られています。
- コンテナを削除するとデータも消える
- 書き込みレイヤーは storage driver を介するためファイル I/O が遅い
- 他のコンテナやホストからデータを参照しにくい
これらを解決するために、コンテナの外側にデータを置くのが Volume と Bind Mount です。
Volume(ボリューム)とは
Volume は Docker が管理する永続化領域 です。ホスト上のストレージ領域に作られますが、その実体のパスを利用者が直接意識する必要はありません。利用者は「名前」で扱います。
# ボリュームを作成 docker volume create app_data # 一覧確認 docker volume ls # 詳細(実体のパスなどを確認) docker volume inspect app_data
Volume を使ってコンテナを起動する
# -v(短い構文) docker run -d \ -v app_data:/var/lib/mysql \ mysql:8.0 # --mount(長い構文・推奨) docker run -d \ --mount type=volume,source=app_data,target=/var/lib/mysql \ mysql:8.0
--mount は type / source / target をキーで明示するため可読性が高く、Docker 公式でも長い構文が推奨されています。
Bind Mount(バインドマウント)とは
Bind Mount は ホスト上の任意のパスを、そのままコンテナ内にマウントする 仕組みです。管理の主体はホスト側(=利用者)で、マウント元のディレクトリやファイルは既にホストに存在しているものを使います。
# -v(短い構文): ホストの絶対パスを指定 docker run -d \ -v /home/user/project/src:/app/src \ node:20 # --mount(長い構文) docker run -d \ --mount type=bind,source=/home/user/project/src,target=/app/src \ node:20
ポイントは source にホストの 絶対パス を指定する点です。Volume のように「名前」ではなく「場所」を指定します。
両者の比較
| 観点 | Volume | Bind Mount |
|---|---|---|
| 管理主体 | Docker | ホスト(利用者) |
| source の指定 | ボリューム名 | ホストの絶対パス |
| 実体の場所 | Docker 管理領域 | ホスト上の任意の場所 |
| 事前準備 | 不要(自動作成される) | マウント元が存在する前提 |
| ホストからの直接編集 | 想定しない | 容易(ホストのファイルそのもの) |
| ポータビリティ | 高い(環境依存しにくい) | 低い(ホストのパス構造に依存) |
| 主な用途 | DB など本番データの永続化 | 開発時のソース共有、設定ファイル注入 |
使い分けの指針
Volume が向くケース
- データベースのデータ永続化(MySQL / PostgreSQL など)
- 本番環境でのデータ保持
- ホストの OS・ディレクトリ構成に依存させたくない場合
- バックアップやボリュームドライバで管理したい場合
Volume は Docker が管理領域を抽象化してくれるため、ホスト環境の差異を気にせず運用できます。本番のデータ永続化では基本的に Volume を選びます。
Bind Mount が向くケース
- 開発時にソースコードをコンテナと共有する(ホストで編集 → コンテナに即反映)
- ホスト側の設定ファイルをコンテナに注入する
- ログをホスト側の決まった場所に出す
開発では、ホストのエディタで書いたコードをそのままコンテナで実行したいので Bind Mount が便利です。一方で、ホストのパスに依存するためチーム間での可搬性は落ちます。
Docker Compose での書き方
services:
db:
image: mysql:8.0
volumes:
# Volume: 名前付きボリューム
- db_data:/var/lib/mysql
app:
image: node:20
volumes:
# Bind Mount: ホストの相対/絶対パス
- ./src:/app/src
working_dir: /app
# 名前付きボリュームはトップレベルで宣言する
volumes:
db_data:
db_data:/var/lib/mysql のように左側が「名前」なら Volume、./src:/app/src のように左側が「パス」なら Bind Mount になります。名前付きボリュームはトップレベルの volumes: に宣言が必要です。
補足: tmpfs mount
永続化手段とは目的が異なりますが、第3の選択肢として tmpfs mount もあります。これはホストのメモリ上にマウントする仕組みで、ディスクに書き込みたくない一時データに使います。コンテナ停止で消えるため永続化用途ではありません。
docker run -d \ --mount type=tmpfs,target=/app/tmp \ nginx
確認方法
マウントが意図通りか確認するには docker inspect を使います。
docker inspect <コンテナID> --format '{{json .Mounts}}'
出力の Type が volume か bind かで、どちらでマウントされているかを確認できます。Source(ホスト側)と Destination(コンテナ側)も合わせて確認すると確実です。
注意点
- Bind Mount のパスは環境依存: 絶対パスがチームや環境で異なると動かない。Compose では相対パス(
./)を使うと緩和できる。 - Bind Mount でマウント先を上書きする: コンテナ内の既存ディレクトリに Bind Mount すると、元のディレクトリ内容は隠れる。
node_modulesを丸ごと上書きしてしまう事故が起きやすい(匿名ボリュームで退避するのが定番)。 - 権限(UID/GID)の差異: Bind Mount ではホストとコンテナのユーザー権限の食い違いで書き込めないことがある。
-vの暗黙作成:-vで存在しない名前を指定すると Volume が自動作成されるが、存在しないホストパスを指定するとディレクトリが作られる。挙動が紛らわしいので--mountを推奨。
まとめ
- Volume = Docker が管理する永続化領域。名前で指定。本番データの永続化向き。環境非依存で可搬性が高い。
- Bind Mount = ホストの任意パスを直接マウント。絶対パスで指定。開発時のソース共有や設定注入向き。ホスト依存。
- 迷ったら「Docker に任せたい=Volume」「ホストのファイルをそのまま使いたい=Bind Mount」で判断する。
- 構文は可読性・事故防止の観点から
--mountを推奨。
目的が「永続化」なのか「ホストとの共有」なのかを意識すると、選択で迷わなくなります。
