Docker の volume と bind mount の違い|どっちを使う?書き方とデータが消える原因

PR
Docker の volume と bind mount の違い|どっちを使う?書き方とデータが消える原因
この記事は約42分で読めます。

Docker の volume と bind mount の違いは、データの置き場所です。volume は Docker が作って管理する置き場で、bind mount はホスト(コンテナの外側。手元のパソコンで Docker を動かしているなら、そのパソコン)にあるファイルやディレクトリ(フォルダ)をそのままコンテナにつなぐ仕組みです。公式のドキュメントは、コンテナが作って使うデータを残すなら volume が推奨の仕組み、コンテナとホストの両方からファイルを触るなら bind mount を使う、と説明しています。

この記事は、docker compose で開発環境を作り始めて、「DB のデータが消えた」「ファイルを変えてもコンテナに反映されない」で困っている人向けです。違いと使い分け、-v と --mount、compose.yaml の書き方、つまずきやすい点を、公式ドキュメントの説明にそって整理します(2026年10月時点)。

この記事でわかること
  • volume・bind mount・tmpfs の置き場所と、コンテナを消したときの違い(比較表つき)
  • 公式が説明している使い分け(残すデータは volume、ホストと両方から触るなら bind mount)
  • -v と --mount の書き方の違いと、ホストに無いディレクトリを渡したときの動き
  • compose.yaml での書き方と、volume の名前の決まり
  • データが消える・変更が反映されないときに、公式の資料から確かめられる原因
  1. コンテナのデータはどこに残る?
  2. volume・bind mount・tmpfs の違い(比較表)
    1. volume とは?
    2. bind mount とは?
    3. tmpfs とは?
    4. 3 つを表で比べる
    5. 中身のあるディレクトリに重ねたときの違い
  3. volume と bind mount はどっちを使う?
  4. -v と --mount の違いと書き方
    1. ホストに無いパスを渡したときの違い
    2. ./ を付けないと volume になる
  5. compose.yaml での volume・bind mount の書き方
    1. 短い書き方(コロン区切り)
    2. 長い書き方(項目で書く)
    3. トップレベルの volumes(名前つき volume の宣言)
    4. volume の名前には project 名が付く
  6. volume を確かめる・消すには?(ls・inspect・rm・prune・df)
    1. 一覧と詳しい情報(ls・inspect)
    2. どの種類でつながっているか(docker inspect の Mounts)
    3. volume を消す(rm)
    4. 使っていない volume をまとめて消す(prune)
    5. volume の大きさ(df)
  7. データが消える・変更が反映されないときに見ること
    1. データが消えたと感じたとき
    2. マウントする場所を間違えると、データが残らない(postgres の例)
    3. 変更が反映されない・ファイルが見えないとき
  8. Mac・Windows(Docker Desktop)で気をつけること
    1. Mac と Windows(Hyper-V)に共通すること
    2. Mac
    3. Windows(WSL 2)
  9. よくある質問
    1. volume をバックアップするには?
    2. 読み取り専用でマウントするには?
    3. ファイル 1 つだけを bind mount できる?
    4. compose の volumes にある :z は何?
  10. まとめ
  11. 参考資料

コンテナのデータはどこに残る?

Docker のコンテナの中で作ったファイルは、既定では「書き込みできるコンテナの層」に保存されます。コンテナのもとになるイメージの層は読み取り専用で、変更できません。その上に重なっているのが、この書き込みの層です。

この層に書いたデータは、コンテナを消すと残りません。書き込みの層はコンテナごとに別のものです。また、書き込みの層のデータをホストや別のコンテナへ取り出すのは簡単ではない、と公式は説明しています。入門ガイドでも、各コンテナはファイルを作る・変える・消すことができ、それがほかのコンテナには影響しないこと、コンテナを消すとその変更も消えることが説明されています。データを残したい例として挙がっているのは、DB のコンテナです。

では、止めただけでも消えるのでしょうか。公式の説明では、書き込みの層のデータが残らなくなるのは、コンテナを消した(destroy した)ときです。Docker Engine 28.3.2(Mac・Docker Desktop)では、docker stop で止めてから docker start で動かし直したコンテナに、書き込みの層に作ったファイルが残っていました。docker rm -f で消してから同じイメージで作ったコンテナには、そのファイルはありませんでした。コンテナの停止と削除の違いは、docker と docker compose の違いの記事の「ちなみに削除と停止の違い」でも説明しています。

書き込みの層の外にデータを置く方法として、公式はストレージの概要で次の 5 つの種類(マウント)を挙げています。「マウント」は、データの置き場所をコンテナの中のディレクトリ(またはファイル)として見えるようにつなぐことです。

  • volume mounts(volume)
  • bind mounts(bind mount)
  • tmpfs mounts(tmpfs)
  • image mounts
  • named pipes

どの種類を選んでも、コンテナの中からは、ディレクトリか 1 つのファイルとして同じように見えます。この記事では、volume・bind mount・tmpfs の 3 つを比べます。image mount と named pipe は、比較表のあとで一言ふれます。

volume・bind mount・tmpfs の違い(比較表)

この 3 つを 1 枚に並べた比較表は、公式のドキュメントには(2026年10月時点で)見当たりません。ここでは、公式の volume・bind mount・tmpfs のページの説明をもとに、先に 1 つずつ説明し、そのあとでこの記事で表にまとめます。

volume とは?

volume は、Docker が作って管理する、コンテナのための永続的なデータの置き場です。docker volume create で明示的に作ることも、コンテナやサービスを作るときに Docker が作ることもできます。まだ無い volume の名前を指定してコンテナを起動したときも、Docker がその volume を作ります。

volume は Docker ホストの上のディレクトリに置かれ、そのディレクトリがコンテナにマウントされます。bind mount に似ていますが、volume は Docker が管理し、ホストの中核の機能から切り離されている点が違います。使っていたコンテナを消してもデータは残り、volume の中身はコンテナの寿命の外にあります。1 つの volume を複数のコンテナに同時にマウントすることもでき、使うコンテナが無くなっても自動では消えません。bind mount と違い、どのコンテナとも関係なく作ったり管理したりできます。

volume には、名前つき(named)と匿名(anonymous)があります。匿名 volume には、そのホストの中で重ならないランダムな名前が付きます。名前つきと同じく、匿名 volume も、使っていたコンテナを消しても残ります。ただし --rm を付けて作ったコンテナ(終わると自動で消えるコンテナ)なら、一緒に消えます。匿名 volume はコンテナごとに新しく作られ、自動では使い回されません。

volume の中身を扱うときは、コンテナにマウントします。volume のデータを直接触るのはサポートされておらず、volume やそのデータが予期しない形で壊れることがある、と公式は書いています。

bind mount とは?

bind mount は、ホストにあるファイルやディレクトリを、そのままコンテナにマウントする仕組みです。volume が Docker の置き場の中に新しいディレクトリを作るのに対して、bind mount はホストにあるものを使います。

ホストのパスとコンテナを直接つなぐので、ホストのどこにあるファイルやディレクトリでも使えます。Docker が切り離していないため、ホスト側のプロセスとコンテナの中のプロセスの両方から、マウントしたファイルを同時に書き換えられます。

次は、Docker Engine 28.3.2(Mac・Docker Desktop)の出力です。コンテナが書いたファイルを Mac から読めること、動いているコンテナにつないだディレクトリを Mac で書き換えると、その内容がコンテナの中からすぐ読めることが分かります。sleep 120 はコンテナを動かしたままにするため、docker exec は動いているコンテナの中で cat を実行してファイルを読むために使っています。

$ docker run --rm --mount type=bind,src=$PWD/hostdir,dst=/data alpine sh -c "echo from-container > /data/c.txt"
$ cat hostdir/c.txt
from-container
$ docker run -d --name blogcheck-bind --mount type=bind,src=$PWD/hostdir,dst=/data alpine sleep 120
60b4ae64449f7a687537fbeffe931c5e978bf34cf29165eb2adbde810fe372cf
$ echo from-host-1 > hostdir/h.txt
$ docker exec blogcheck-bind cat /data/h.txt
from-host-1
$ echo from-host-2 > hostdir/h.txt
$ docker exec blogcheck-bind cat /data/h.txt
from-host-2

tmpfs とは?

tmpfs mount は、ファイルをホストのメモリに直接置き、ディスクには書かない仕組みです。データは一時的なもので、コンテナが止まる・再起動する、またはホストを再起動すると消えます。Docker Engine 28.3.2(Mac・Docker Desktop)の出力でも、docker restart のあとには、tmpfs に書いたファイルが消えていました。

公式は、tmpfs は一時的なメモリ上の置き場が要る場面に向き、使うのは今のコンテナの間だけ持てばよいデータに限る、と説明しています。公式は、途中のデータのキャッシュ、認証情報のような機密、ディスクの I/O を減らしたいときを例に挙げています。ホストにもコンテナにも残したくないデータ向けで、セキュリティのため、または残す必要のないデータを大量に書くときの性能のために使う、とも説明しています。

tmpfs が使える環境について、公式は、Linux で Docker を動かしているときだけ使える機能だと書いています。一方、Docker Engine 28.3.2(Mac・Docker Desktop)では、--tmpfs も --mount type=tmpfs も tmpfs としてマウントされました。

3 つを表で比べる

次の表は、ここまでの公式の説明を、この記事でまとめたものです。

比べるところvolumebind mounttmpfs
置き場所Docker ホストの上の、Docker が管理するディレクトリ(Docker の置き場の中に新しく作られる)ホストにあるファイルやディレクトリ(ホストのどこにあるものでも使える)ホストのメモリ(ディスクには書かない)
コンテナを消したときデータは残る(--rm を付けて作ったコンテナの匿名 volume は一緒に消える)ホストにあるファイルをつないでいるので、コンテナで作ったファイルをホストに残したいときに使える消える(止まる・再起動する・ホストを再起動するときも)
中身のあるディレクトリに重ねたとき空の volume ならコンテナ側のファイルが既定でコピーされる。中身のある volume ならもとのファイルは隠れるもとの中身は隠れる(volume と動きが違う)もとのファイルは隠れる
向いている使い方残したいデータ、バックアップや移行、高い I/O 性能が要るとき、複数のコンテナでの共有ホストの開発環境とコンテナでソースコードやビルドの成果物を共有する、ホストの設定ファイルをコンテナに渡す今のコンテナの間だけで要らなくなるデータ(キャッシュ、機密、ディスク I/O を減らしたいとき)
気をつけることホストからファイルを触りたいときには向かない。volume のデータを直接触るのはサポート外既定でホストのファイルに書き込めるのでセキュリティに関わる(readonly か ro で止められる)。ホストに強く結びつき、同じディレクトリ構成が無い別のホストでは失敗することがあるコンテナどうしで共有できない。コンテナのメモリの上限に数えられる。スワップに書かれて残ることがある

ほかに、公式は image mount と named pipe も挙げています。image mount は、別のイメージの中身を読み取り専用でコンテナに見せる仕組みで、containerd image store が必要です。--mount type=image は Docker Engine 28.0.0 から使えます。named pipe は、Docker ホストとコンテナの通信に使える仕組みです。この記事では扱いません。

中身のあるディレクトリに重ねたときの違い

コンテナの中にすでにファイルがあるディレクトリにマウントを重ねたときの動きは、種類によって違います。

  • 空の volume を重ねると、コンテナ側にあったファイルやディレクトリが、既定で volume にコピーされます。コピーさせたくないときは volume-nocopy を使います。
  • 中身のある volume を重ねると、もとのファイルは隠れます。マウントを外して、隠れたファイルを見せ直す簡単な方法は無く、マウント無しでコンテナを作り直すのがいちばんよい、と公式は書いています。
  • bind mount を重ねると、もとの中身は隠れます。公式は、この動きは volume と違い、驚くことがある、とも書いています。
  • tmpfs も、もとのファイルを隠します。

次は、Docker Engine 28.3.2(Mac・Docker Desktop)の出力です。alpine イメージ(このときの alpine:latest=Alpine 3.24.2)の /etc/apk には、もともと 5 つのファイルとディレクトリがあります。まだ無い名前の volume blogcheck-pop を重ねると、空の volume として作られ、5 つがコピーされます。3 つ目のコマンドは、その volume を /mnt につないで中身を見ています。

$ docker run --rm alpine ls /etc/apk
arch
keys
protected_paths.d
repositories
world
$ docker run --rm -v blogcheck-pop:/etc/apk alpine ls /etc/apk
arch
keys
protected_paths.d
repositories
world
$ docker run --rm -v blogcheck-pop:/mnt alpine ls /mnt
arch
keys
protected_paths.d
repositories
world

次は、hello.txt だけが入った volume blogcheck-x を /etc/apk に重ねた場合と、空のホストのディレクトリ hostdir-empty を bind mount で重ねた場合です。volume の中身が見え、イメージ側のファイルは見えなくなります。bind mount のほうは中が空に見え、コマンドの前後でホストのディレクトリも空のままで、コンテナ側のファイルはコピーされませんでした。

$ docker run --rm -v blogcheck-x:/data alpine sh -c "echo hello > /data/hello.txt"
$ docker run --rm -v blogcheck-x:/etc/apk alpine ls /etc/apk
hello.txt
$ docker run --rm --mount type=bind,src=$PWD/hostdir-empty,dst=/etc/apk alpine ls -A /etc/apk

volume と bind mount はどっちを使う?

公式のドキュメントは、使い分けを次のように説明しています。

  • コンテナが作って使うデータを残すなら、volume が推奨の仕組みです。bind mount はホストのディレクトリ構成と OS に左右されますが、volume は Docker がすべて管理します。
  • ホストからファイルを触りたいときは、volume は向きません(volume は Docker がすべて管理するため)。コンテナとホストの両方からファイルを触るなら、bind mount を使います。

入門ガイドは、次のように説明しています。コンテナの中で作ったり変えたりしたデータを、コンテナが止まったあとも確実に残したいなら volume です。設定ファイルや開発中のコードのように、ホストの決まったファイルやディレクトリをコンテナと直接共有したいなら bind mount です。bind mount は、ホストとコンテナでリアルタイムにファイルを触る開発環境に向く、とあります。

次の表は、こうした公式の言い回しを、よくある場面に当てはめたこの記事のまとめです。

こんなとき選ぶもの公式の説明
DB のデータなど、コンテナが作って使うデータを残したいvolumeコンテナが作って使うデータを残すなら、volume が推奨の仕組み。入門ガイドも、コンテナが止まったあとも確実に残したいなら volume と説明している
ホストで編集しているソースコードや設定ファイルを、コンテナで動かしたいbind mountコンテナとホストの両方からファイルを触るなら bind mount。入門ガイドは、設定ファイルや開発中のコードを直接共有したいなら bind mount と説明している
今のコンテナの間だけで要らなくなるデータtmpfs公式は、tmpfs を使うのは今のコンテナの間だけ持てばよいデータに限る、と説明している

volume には、それを使うコンテナのサイズを増やさず、コンテナの書き込みの層に書くより速い、という良い点もあります。書き込みの層に書くには、ストレージドライバーがファイルシステムを管理する必要があるためだ、と公式は説明しています。

bind mount を使うときは、既定でホストのファイルに書き込める点に注意が必要です。コンテナからホストの大事なファイルを作る・変える・消すこともでき、セキュリティに関わる、と公式は書いています。書き込ませたくないときは、readonly か ro を付けます。

Mac と Windows(Hyper-V)の Docker Desktop では、速さも関わってきます。Docker Desktop の設定のページ(この説明は Mac・Linux・Windows の Hyper-V 向け)は、共有フォルダは、ホストでコードを編集しながらコンテナで動かすためのものだと説明しています。キャッシュのディレクトリや DB のようなコード以外のものは、名前つき volume を使って Linux の VM の中に置いたほうが、性能がずっとよくなる、とも書いています。くわしくは、後ろの「Mac・Windows(Docker Desktop)で気をつけること」にまとめています。

-v と --mount の違いと書き方

volume と bind mount は、どちらも docker run に -v(--volume)か --mount を付けて指定できます。tmpfs は --tmpfs か --mount です。公式の書き方は次のとおりです。

# volume
docker run --mount type=volume,src=<volume-name>,dst=<mount-path>
docker run --volume <volume-name>:<mount-path>

# bind mount
docker run --mount type=bind,src=<host-path>,dst=<container-path>
docker run --volume <host-path>:<container-path>

# tmpfs
docker run --mount type=tmpfs,dst=<mount-path>
docker run --tmpfs <mount-path>

--mount は、<key>=<value> をカンマで区切って並べる書き方で、キーの順番は自由です(source は src、destination は dst や target とも書けます)。-v は、コロンで区切った 3 つの欄を、決まった順番で書きます。

比べるところ-v(--volume)--mount
書き方コロン区切りの 3 つの欄を、順番どおりに書く<key>=<value> をカンマ区切りで並べる。順番は自由
公式の説明基本的な volume・bind mount の操作なら、簡単で便利(入門ガイド)明示的で、使えるオプションがすべてそろっている(volume・bind mount のページ)。細かい制御ができ、複雑な場合や本番の配備に向く(入門ガイド)
ホストに無いパス(bind mount)Docker がホストにディレクトリを作る既定ではエラー。bind-create-src を付けると作る(Docker Engine 29.3.0 から)

volume では、volume ドライバーのオプションの指定、volume の中のサブディレクトリのマウント、Swarm のサービスへのマウントは、--mount でしかできません。

公式が --mount を勧める言い方は、ページによって強さが少しずつ違います。それぞれの言い方のまま紹介します。

  • volume と bind mount のページ: 一般に --mount が推奨です。主な違いは、--mount のほうが明示的で、使えるオプションがすべてそろっていることです。
  • tmpfs のページ: 一般に --mount が推奨です。ただし --tmpfs は短く書け、より多くのマウントオプションを指定できます。
  • docker run のリファレンス: --volume を廃止する予定は無いが、--mount を使うことを推奨しています。
  • 入門ガイド: Docker は -v より --mount を推奨しています。マウントをよりきちんと制御でき、ディレクトリが無いときの問題を避けられるためです。

--mount で type を書かなかったときは、volume になります(docker service create のリファレンスの説明)。docker run の公式の例にも、type を省いた --mount source=myvol2,target=/app が、-v myvol2:/app と同じ結果になる例があります。

ホストに無いパスを渡したときの違い

bind mount でホストに無いパスを渡したときの動きは、-v と --mount で違います。

  • -v(--volume): Docker がホストにディレクトリを作ります。ファイルのつもりのパスでも、いつもディレクトリとして作られます。Docker のデーモン(コンテナを動かす本体)にディレクトリを作る権限が無いときは、先に作っておきます。docker run のリファレンスにも、同じ説明があります。
  • --mount: 既定ではディレクトリを作らず、エラーになります。
  • --mount に bind-create-src を付けると、ホストにディレクトリを作ります。このオプションは Docker Engine 29.3.0(2026年3月5日)で追加されました。

次は、Docker Engine 28.3.2(Mac・Docker Desktop)の出力です。長いパスは … で縮めています。1 つ目の -v では、ホストに無かったディレクトリが作られ、Mac 側にもディレクトリができました。2 つ目の --mount はエラーになり、ディレクトリは作られませんでした。3 つ目は、bind-create-src が 28.3.2 には無いため、エラーになります。

$ docker run --rm -v $PWD/nonexist-v:/data alpine ls -ld /data
drwxr-xr-x    2 root     root            64 Oct  3 09:57 /data
$ docker run --rm --mount type=bind,src=$PWD/nonexist-m,dst=/data alpine ls -ld /data
docker: Error response from daemon: invalid mount config for type "bind": bind source path does not exist: /host_mnt/private/tmp/…/nonexist-m

Run 'docker run --help' for more information
$ docker run --rm --mount type=bind,src=$PWD/nonexist-c,dst=/data,bind-create-src alpine ls -ld /data
invalid argument "type=bind,src=/private/tmp/…/nonexist-c,dst=/data,bind-create-src" for "--mount" flag: invalid field 'bind-create-src' must be a key=value pair

Usage:  docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

Run 'docker run --help' for more information

./ を付けないと volume になる

Docker Engine 23 から、-v でホストの相対パスが使えます(親のディレクトリを指す ../ は 28.2.0 から。-v と --mount type=bind の両方で使えます)。ただし、-v の 1 つ目の欄に ./ を付けずに名前だけを書くと、bind mount ではなく名前つき volume になります。Docker Engine 28.3.2 では、同じ名前のディレクトリが今の場所にあっても、volume が作られました。

次は、f.txt の入った blogcheck-reldir というディレクトリが今の場所にある状態での出力です。./ を付けると bind mount になり、付けないと空の volume がマウントされます。

$ docker run --rm -v ./blogcheck-reldir:/data alpine ls /data
f.txt
$ docker run --rm -v blogcheck-reldir:/data alpine ls -A /data
$ docker volume ls --filter name=blogcheck-reldir
DRIVER    VOLUME NAME
local     blogcheck-reldir

compose.yaml での volume・bind mount の書き方

docker compose では、compose.yaml のサービスの volumes に、ホストのパスや名前つき volume を書いてマウントします。Compose ファイルの既定の名前は、compose.yaml(推奨)か compose.yml です。docker-compose.yaml と docker-compose.yml も、以前の版との互換のために使えます。

先頭の version: は古い(obsolete)項目で、書くと警告が出るだけです。Compose は version に関係なく、常に最新の schema で確かめます。この記事の例には、version: を書いていません。

短い書き方(コロン区切り)

短い書き方は、VOLUME:CONTAINER_PATH、またはアクセスモードを足した VOLUME:CONTAINER_PATH:ACCESS_MODE の形です。VOLUME には、ホストのパス(bind mount)か volume の名前を書きます。ACCESS_MODE は、rw(読み書き。何も書かなければこれ)・ro(読み取り専用)・z・Z です。

次の compose.yaml では、db-data が名前つき volume、./src がホストの相対パス(bind mount)です。一番下の volumes: については、このあと説明します。

services:
  db:
    image: alpine
    volumes:
      - db-data:/var/lib/data
      - ./src:/app
volumes:
  db-data:
  • 相対パスは、Compose ファイルのあるディレクトリから解決されます。そのため、使えるのはローカルで動かす場合だけです。
  • 名前つき volume と紛れないように、相対パスは常に . か .. から書き始めるのがよい、と公式は書いています。
  • 短い書き方で bind mount するとき、ホストにそのパスが無ければ、ディレクトリを作ります(以前の docker-compose との互換のため)。作らせたくないときは、長い書き方で create_host_path を false にします。

docker compose config を実行すると、書いた内容が展開されて表示されます。次は、上の compose.yaml を blogcheck-proj というディレクトリに置いたときの、Docker Compose v2.38.2(Docker Desktop 4.43.2)の出力です。長いパスは … で縮めています。

$ docker compose config
name: blogcheck-proj
services:
  db:
    image: alpine
    networks:
      default: null
    volumes:
      - type: volume
        source: db-data
        target: /var/lib/data
        volume: {}
      - type: bind
        source: /private/tmp/…/blogcheck-proj/src
        target: /app
        bind:
          create_host_path: true
networks:
  default:
    name: blogcheck-proj_default
volumes:
  db-data:
    name: blogcheck-proj_db-data

見どころは 3 つです。1 つ目は、./src が compose.yaml のあるディレクトリの絶対パスに変わっていることです。2 つ目は、短い書き方の bind mount に create_host_path: true が付いていることで、ホストに src が無ければ作られます。3 つ目は、volume の名前が blogcheck-proj_db-data になっていることです。name: blogcheck-proj は project 名で、この例ではディレクトリの名前が使われています。このコマンドは何も作らず、実行後もディレクトリには compose.yaml だけがあり、volume も増えませんでした。

長い書き方(項目で書く)

長い書き方は、type・source・target などを項目として書く形です。type には volume・bind・tmpfs・image・npipe・cluster が書けます。read_only のほか、種類ごとの設定(bind の propagation・create_host_path・selinux、volume の nocopy・subpath、tmpfs の size・mode、image の subpath)と consistency があります。

次は、bind mount を長い書き方で書いた例です。公式のbind mount のページの例から、使われていない volumes: の myapp: の宣言を省いています。

services:
  frontend:
    image: node:lts
    volumes:
      - type: bind
        source: ./static
        target: /opt/app/static

create_host_path の既定は true です。長い書き方でも、書かなければ、ホストにディレクトリが無いときに作ります。これは、無いパスをエラーにする docker run --mount と違う点です。Docker Compose v2.38.2 では、create_host_path を書かなかったときはディレクトリが作られ、false にするとエラーになります。なお、長い書き方で書かなかったときは、docker compose config の表示に create_host_path は出ません。それでも docker compose up をすると、ディレクトリは作られます。

トップレベルの volumes(名前つき volume の宣言)

複数のサービスで volume を使い回すときは、トップレベル(services: と同じ、字下げしない階層)の volumes に名前つき volume を宣言します。1 つのサービスだけで使うホストのパスは、サービスの中に書けば足ります。次は、公式の例です。db-data を、backend と backup の 2 つのサービスでマウントしています。

services:
  backend:
    image: example/database
    volumes:
      - db-data:/etc/data

  backup:
    image: backup-service
    volumes:
      - db-data:/var/lib/backup/data

volumes:
  db-data:
  • トップレベルの volumes の項目は、空でもかまいません。そのときは、コンテナエンジンの既定の設定で volume を作ります。
  • docker compose up は、volume が無ければ作り、あれば使います。Compose の外で消されていたら、作り直します。最初の docker compose up で volume が作られ、次からは同じ volume が使われます。
  • Compose の外で作った volume を使うときは、その項目に external: true を書きます。Compose は volume を作らず、無ければエラーを返します。name 以外の項目を書くと、compose ファイルが無効になります。

volume の名前には project 名が付く

Compose が作る volume の名前には、project 名が付きます。公式の例では、db-data を {project_name}_db-data という名前で作ります。external を付けたときは、db-data という名前のまま探します。project 名の既定は、Compose ファイルのあるディレクトリの名前です。決め方の優先順は、高い順に次のとおりです。

  1. コマンドの -p
  2. 環境変数 COMPOSE_PROJECT_NAME
  3. トップレベルの name:
  4. Compose ファイルのあるディレクトリの名前
  5. Compose ファイルを指定しないときは、今いるディレクトリの名前

トップレベルの volumes で volume に name: を付けると、その名前がそのまま使われ、project 名は付きません。ディレクトリの名前が blogcheck-proj2 のときの docker compose config では、volume の名前は blogcheck-proj2_db-data と表示されました。project 名が変わると、volume の名前も変わります。今ある volume の名前は、docker volume ls で確かめられます。

volume を確かめる・消すには?(ls・inspect・rm・prune・df)

volume を作る・一覧にする・詳しく見る・消すコマンドは、次の 4 つです(公式の例)。

docker volume create my-vol
docker volume ls
docker volume inspect my-vol
docker volume rm my-vol

最後の docker volume rm は、volume と中のデータを消す操作です(下の「volume を消す(rm)」の注意も読んでください)。

一覧と詳しい情報(ls・inspect)

次は、Docker Engine 28.3.2(Mac・Docker Desktop)の出力です。ls は、名前が blogcheck- で始まるものだけに絞っています。

$ docker volume create blogcheck-x
blogcheck-x
$ docker volume ls --filter name=blogcheck-
DRIVER    VOLUME NAME
local     blogcheck-x
$ docker volume inspect blogcheck-x
[
    {
        "CreatedAt": "2026-10-03T09:56:56Z",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/blogcheck-x/_data",
        "Name": "blogcheck-x",
        "Options": null,
        "Scope": "local"
    }
]

Mountpoint は、volume の置き場所です。公式の例も、この出力も、/var/lib/docker/volumes/<名前>/_data の形です。ただし、Mac の Docker Desktop では、この場所を Mac の ls で見ても、次のように見つかりません。

$ ls -ld /var/lib/docker/volumes/blogcheck-x/_data
ls: /var/lib/docker/volumes/blogcheck-x/_data: No such file or directory

Mac の Docker Desktop は、Linux のコンテナとイメージを、Mac の中の大きな 1 つの「ディスクイメージ」ファイルに置きます。Linux 上の Docker がふつう /var/lib/docker に置くのとは違います。volume が Mac のどこに置かれるかは、公式のドキュメントでは見つかりませんでした(2026年10月時点)。volume のデータを直接触るのはサポート外なので、中身を見るときは、コンテナにマウントするか、Docker Desktop のVolumes の画面を使います。この画面では、volume を作る・調べる・消す・複製する・空にする・書き出す・読み込むことができ、中のファイルやディレクトリも見られます。

どの種類でつながっているか(docker inspect の Mounts)

コンテナのマウントは、docker inspect <コンテナ> の Mounts で確かめられます。Type(volume・bind・tmpfs の別)、Source、Destination、RW などが出ます。

次は、--format '{{json .Mounts}}' を付けて Mounts の部分だけを出した、Docker Engine 28.3.2(Mac・Docker Desktop)の出力です。1 つ目は bind mount のコンテナ(前に出した blogcheck-bind)、2 つ目は -v /data のようにコンテナ側のパスだけを書いて作ったコンテナです。長いパスと volume の名前は … で縮めています。

$ docker inspect blogcheck-bind --format '{{json .Mounts}}'
[{"Type":"bind","Source":"/private/tmp/…/hostdir","Destination":"/data","Mode":"","RW":true,"Propagation":"rprivate"}]
$ docker run --name blogcheck-anon1 -v /data alpine true
$ docker inspect blogcheck-anon1 --format '{{json .Mounts}}'
[{"Type":"volume","Name":"500a35658eb1…","Source":"/var/lib/docker/volumes/500a35658eb1…/_data","Destination":"/data","Driver":"local","Mode":"","RW":true,"Propagation":""}]

2 つ目では、Type が volume で、ランダムな名前の匿名 volume が付いています。

volume を消す(rm)

docker volume rm <名前> で消します。コンテナが使っている volume は消せません。次は、Docker Engine 28.3.2(Mac・Docker Desktop)の出力です。docker create で作っただけの、止まっているコンテナが指しているだけでも、volume is in use のエラーになります。その volume を使っているコンテナを、先に消す必要があります。

注意: docker volume rm は、volume と中のデータを消します。必要なデータは先にバックアップしておきましょう(方法は「よくある質問」にあります)。

$ docker create --name blogcheck-user -v blogcheck-x:/data alpine true
82f95cc8ab0adced25dcf448719a588dd9053eef8b41941f15c8b4a56bb74e4b
$ docker volume rm blogcheck-x
Error response from daemon: remove blogcheck-x: volume is in use - [82f95cc8ab0adced25dcf448719a588dd9053eef8b41941f15c8b4a56bb74e4b]
$ docker rm blogcheck-user
blogcheck-user

使っていない volume をまとめて消す(prune)

docker volume prune は、どのコンテナにも参照されていない、ローカルの volume を消します。既定で消すのは匿名 volume だけです。名前つき volume も消すのは、-a(--all)を付けたときです(API 1.42 以上)。この既定は、Docker Engine 23.0.0(2023年2月1日)からです。なお、volume のページ(「Remove all volumes」)や入門ガイド(「remove all unused volumes」)には、すべての volume を消すように読める書き方もあります。

注意: prune は、volume とその中のデータを消す操作です。-a を付けると、名前をつけて残してある volume も対象になります。実行する前に docker volume ls で一覧を確かめ、必要なデータは先にバックアップしておきましょう(方法は「よくある質問」にあります)。

volume の大きさ(df)

volume の大きさは、docker system df -v の出力の「Local Volumes space usage」で見られます。次は、その部分だけを抜き出した、Docker Engine 28.3.2(Mac・Docker Desktop)の出力です。名前が blogcheck- で始まる volume の行だけを残しています。

$ docker system df -v
Local Volumes space usage:
VOLUME NAME                                                        LINKS     SIZE
blogcheck-auto-m                                                   0         0B
blogcheck-x                                                        0         6B
blogcheck-auto-v                                                   0         0B
blogcheck-pop                                                      0         1.436kB
blogcheck-nocopy                                                   0         0B
blogcheck-reldir                                                   0         0B

データが消える・変更が反映されないときに見ること

ここでは、公式のドキュメントに書かれている原因を並べます。自分の環境の原因を特定するものではないので、当てはまりそうなものから順に確かめてください。

データが消えたと感じたとき

  • マウントしていない場所に書いている: コンテナの書き込みの層に書いたデータは、コンテナを消すと残りません。docker compose up は、設定やイメージが変わったコンテナを止めて作り直します(マウントした volume は保たれます)。
  • docker compose down で volume は消える?: 既定で消えるのは、コンテナ、compose ファイルの networks、既定のネットワークだけです。external の network と volume は、決して消えません。docker compose down -v(--volumes)を付けると、compose ファイルの volumes に宣言した名前つき volume と、コンテナにつながった匿名 volume が消えます。down の基本は、docker と docker compose の違いの記事の「docker composeの停止コマンド」でも説明しています(そちらのコマンドは docker-compose とハイフンを付けた書き方です)。
  • 匿名 volume を使っている: 匿名 volume はコンテナごとに新しく作られ、自動では使い回されません。docker compose down では消えませんが、決まった名前が無いので、次の docker compose up で自動ではマウントされません。更新をまたいで残したいデータは、bind mount か名前つき volume にします。docker compose up に -V(--renew-anon-volumes)を付けると、前のコンテナのデータを引き継がずに、匿名 volume を作り直します。
  • --rm や docker rm -v を付けている: docker run --rm は、コンテナを消すときに匿名 volume も消します。docker rm -v(--volumes)も、コンテナにつながった匿名 volume を消します。どちらも、名前つき volume は消しません。Docker Engine 28.3.2(Mac・Docker Desktop)では、--rm を付けて動かした匿名 volume つきのコンテナを docker stop で止めると、コンテナも匿名 volume も消えました。--rm を付けずに作って docker rm で消したコンテナの匿名 volume は、残りました。
  • docker volume prune を実行した: 既定では、使っていない匿名 volume が消えます。-a(--all)を付けると、使っていない名前つき volume も消えます。
  • tmpfs に置いている: tmpfs のデータは、コンテナが止まる・再起動する、またはホストを再起動すると消えます。

マウントする場所を間違えると、データが残らない(postgres の例)

Dockerfile の VOLUME は、指定した名前でマウントポイントを作り、外から volume を付ける場所として印を付けます。docker run は、新しく作った volume を、イメージのその場所にあるデータで初期化します。

公式イメージの postgres(Docker Hub)の説明が、よい例です。17 以下では、データの volume を /var/lib/postgresql/data にマウントします。/var/lib/postgresql にマウントすると、コンテナを作り直したときにデータが残らない、と書かれています。postgres のイメージは VOLUME を宣言しているので、その場所に何もマウントしないと匿名 volume が作られ、データはそちらに書かれます。匿名 volume は、コンテナを作り直しても、新しいコンテナには引き継がれません。

postgres 18 以上では、場所が変わっています。postgres がデータを置く場所(PGDATA)が版ごとになり(18 は /var/lib/postgresql/18/docker)、VOLUME は /var/lib/postgresql に変わりました。マウントも、新しい場所に向けます。Docker の入門ガイドの例でも、postgres:18 では次のように /var/lib/postgresql にマウントしています(2026年10月時点)。

docker run --name=db -e POSTGRES_PASSWORD=secret -d -v postgres_data:/var/lib/postgresql postgres:18

ほかのイメージを使うときも、データを置く場所は、そのイメージの説明で確かめましょう。

変更が反映されない・ファイルが見えないとき

  • volume を使っている: ホストで編集したファイルをコンテナで使いたいなら、bind mount です。volume は Docker がすべて管理するので、ホストからファイルを触る用途には向きません。volume のデータを直接触るのは、サポート外です。
  • イメージ側のファイルが隠れている: 中身のある volume を重ねると、もとのファイルは隠れます。マウントを外して、隠れたファイルを見せ直す簡単な方法は無く、マウント無しでコンテナを作り直すのがいちばんよい、と公式は書いています。bind mount と tmpfs も、重ねたディレクトリにもとからあるファイルを隠します。前に出した出力では、hello.txt だけが入った volume を /etc/apk に重ねると、ls には hello.txt だけが出ました。
  • Mac・Windows の Docker Desktop の事情がある: 共有の範囲、Windows の WSL 2 でのファイルの置き場所、大文字小文字の違いなどが関わります。次の節にまとめています。
  • Compose Watch に頼っている: Compose Watch は、bind mount の代わりではなく、コンテナで開発するための相棒として用意された機能です。image で指定した作成済みのイメージを使うサービスの変更は、追いません。

Mac・Windows(Docker Desktop)で気をつけること

Docker Desktop では、Docker のデーモン(コンテナを動かす本体)は、ホストの上で直接ではなく、Linux の VM(仮想マシン)の中で動いています。bind mount はクライアントではなくデーモンのホストに作られますが、Docker Desktop には、ホストのディレクトリをコンテナと共有できるように、bind mount を受け渡す仕組みが組み込まれています。

Mac と Windows(Hyper-V)に共通すること

Docker Desktop の設定のページは、次の説明を Mac・Linux・Windows(Hyper-V)向けとしています。

  • 共有フォルダは、ホストでコードを編集しながらコンテナで動かすためのものです。キャッシュのディレクトリや DB のようなコード以外のものは、名前つき volume を使って Linux の VM の中に置くほうが、性能がずっとよくなります。
  • コンテナと共有するのは、必要なディレクトリだけにします。ホストでファイルが変わると、それを VM に知らせる手間がかかるため、共有が多すぎると CPU の負荷が上がり、ファイルの扱いが遅くなります。
  • 2026年10月時点で、有料プラン(Pro・Team・Business)の Docker Desktop には、同期したキャッシュで bind mount の性能を上げる Synchronized file shares があります。10 万ファイル以上の大きなリポジトリなどが、対象として挙げられています。

Mac

  • 既定で共有されるのは、/Users・/Volumes・/private・/tmp・/var/folders です。この外にあるプロジェクトは、共有の一覧に足さないと、実行時に Mounts denied や cannot start service のエラーになることがあります。
  • ファイルが見つからない、マウントが拒否される、サービスが起動しない、といったエラーが出たときの公式の手順は、Settings → Resources → File sharing を開き、Dockerfile やマウントするパスが入っているドライブやフォルダを足すことです。
  • Mac のファイルシステムは既定で大文字と小文字を区別しませんが、Linux は区別します。Docker Desktop は、共有したファイルをもとの大文字小文字のまま開くよう求めるので、test を Test として開こうとすると、"No such file or directory" のエラーになります。

Windows(WSL 2)

  • bind mount するファイルは、Windows 側ではなく Linux のファイルシステムに置いたほうが、性能がずっと高くなります。docker run -v /mnt/c/users:/users のように、Windows 側の /mnt/c を使う書き方は避けます。Linux のシェルから docker run -v ~/my-project:/sources <my-image> のように指定します(WSL 2 のベストプラクティス)。
  • Linux のコンテナがファイルの変更の通知(inotify)を受け取れるのは、もとのファイルが Linux のファイルシステムにあるときだけです。変更を見て自動で読み直す Web 開発の流れは、この通知に頼っています。
  • Windows の PowerShell で -v や --mount を使うときは、./ ではなく、絶対パスを書きます。公式のトラブルシュートには、docker run --rm -ti -v C:\Users\user\work:/work alpine という例があります。
  • ファイルが見つからない、マウントが拒否される、サービスが起動しない、といったエラーが出たときは、Settings の Shared Folders を開きます(Hyper-V で動かしているとき)。
  • Windows から共有したボリュームの権限は、既定で 0777 で、変えられません。違う権限が要るアプリについて、公式は、ホストにマウントしない volume を使うか、既定の権限で動くようにする、という 2 つの直し方を挙げています。

よくある質問

volume をバックアップするには?

公式のドキュメントには、--volumes-from で volume を持つコンテナを指し、ホストの今のディレクトリを /backup にマウントして、tar でまとめる例があります。dbstore は volume を持つコンテナの名前、/dbdata はその volume のパスです。$(pwd) は、Linux と macOS では今のディレクトリに展開されます。

docker run --rm --volumes-from dbstore -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata

戻すときは、--volumes-from で指したコンテナの volume に、tar の中身を展開します。

docker run --rm --volumes-from dbstore2 -v $(pwd):/backup ubuntu bash -c "cd /dbdata && tar xvf /backup/backup.tar --strip 1"

どちらも公式の例で、ここに出力例はありません。Docker Desktop の Volumes の画面にも、volume を書き出す(export)・読み込む(import)機能があります。

読み取り専用でマウントするには?

--mount では readonly(ro でもかまいません)を足し、-v では最後に :ro を足します。bind mount は既定でホストのファイルに書き込めるので、書き込ませたくないときは、この指定を付けます。compose の短い書き方では ACCESS_MODE に ro を、長い書き方では read_only を書きます。

次は、Docker Engine 28.3.2(Mac・Docker Desktop)の出力です。読み取り専用の volume に書き込もうとすると、"Read-only file system" のエラーになります。

$ docker run --rm --mount type=volume,src=blogcheck-x,dst=/data,readonly alpine sh -c "echo x > /data/y.txt"
sh: can't create /data/y.txt: Read-only file system
$ docker run --rm -v blogcheck-x:/data:ro alpine sh -c "echo x > /data/y.txt"
sh: can't create /data/y.txt: Read-only file system

ファイル 1 つだけを bind mount できる?

できます。bind mount は、ホストにあるファイルやディレクトリをコンテナにマウントする仕組みで、コンテナの中からは、ディレクトリか 1 つのファイルとして見えます。

ただし、-v でホストに無いパスを渡すと、ファイル名のつもりでも、いつもディレクトリとして作られます。次は、Docker Engine 28.3.2(Mac・Docker Desktop)の出力です。blogcheck-missing.conf というパスがホストに無い状態で -v に渡すと、コンテナの中の /data/app.conf は、行の先頭が d のディレクトリになりました。ファイルをマウントしたいときは、先にホストにファイルを作っておきます。compose でも、ホストにパスが無ければディレクトリが作られます(短い書き方でも、長い書き方〔既定の create_host_path: true〕でも)。

$ docker run --rm -v $PWD/blogcheck-missing.conf:/data/app.conf alpine ls -ld /data/app.conf
drwxr-xr-x    2 root     root            64 Oct  3 09:58 /data/app.conf

compose の volumes にある :z は何?

短い書き方の VOLUME:CONTAINER_PATH:ACCESS_MODE の最後の欄(ACCESS_MODE)に書ける指定です。z と Z は、SELinux のラベルの設定です。SELinux の無い環境では、この指定は無視されます。なお、SELinux のラベルは、--mount では変えられません。

まとめ

  • volume は、Docker が作って管理する置き場です。公式は、コンテナが作って使うデータを残すなら volume が推奨の仕組み、と説明しています
  • bind mount は、ホストのファイルやディレクトリをそのままつなぐ仕組みです。コンテナとホストの両方からファイルを触るときに使います。tmpfs は、ホストのメモリに置く一時的な置き場です
  • 公式は --mount を推奨していますが、--volume(-v)を廃止する予定は無い、とも書いています。ホストに無いパスを渡したときは、-v はディレクトリを作り、--mount は既定でエラーになります
  • compose には、短い書き方、長い書き方、トップレベルの volumes があります。volume の名前には project 名が付きます。先頭の version: は古い項目なので、例には書きません
  • データが消えたと感じたら、書き込みの層に書いていないか、匿名 volume、down -v、--rm、prune、マウントする場所を見ます。変更が反映されないときは、volume を使っていないか、イメージ側のファイルが隠れていないかを見ます
  • Mac・Windows の Docker Desktop では、共有の範囲、WSL 2 でのファイルの置き場所、大文字小文字の違いに気をつけます

参考資料

※この記事は、公式の資料をもとに AI(Claude)で下書きし、2026年10月3日時点の公式の資料と照らして確かめてから公開しています。誤りに気づいたらお問い合わせからお知らせください。

PR
PR
タイトルとURLをコピーしました