目次
Docker Volume 入門:Volume と bind mount でデータを永続化
コンテナ内で書き込んだファイルは、コンテナの書き込み可能レイヤーに保存されます。コンテナを削除すると、データも一緒に消えてしまいます。データベースやユーザーがアップロードしたファイルなど、保存が必要なデータはコンテナの外に置く必要があります。Docker には次の3種類のマウント方法があります。
| 種類 | データの保存場所 | 適した用途 |
|---|---|---|
| Volume | Docker が管理する領域 | データベース、本番環境で永続化するデータ |
| Bind mount | ホスト上で指定したフォルダー | 開発中のコード同期、ホストの設定ファイルの読み込み |
| tmpfs | メモリ | 一時データ、ディスクに書き込みたくないデータ |
Volume:コンテナを削除してもデータは残る
PostgreSQL を使って実際に確認します。データベースを起動し、pgdata という名前の Volume にデータを保存します。
docker run -d --name db -e POSTGRES_PASSWORD=secret -v pgdata:/var/lib/postgresql postgres:18
-v pgdata:/var/lib/postgresql は、pgdata という名前の Volume をコンテナ内の /var/lib/postgresql にマウントするという意味です。Volume が存在しない場合、Docker が自動的に作成します。
テーブルを作成してデータを1件書き込み、コンテナを削除してから、同じ Volume を使って新しいコンテナを起動します。
$ docker run -d --name db -e POSTGRES_PASSWORD=secret -v pgdata:/var/lib/postgresql postgres:18
119ed37ba5c6dc12a773a32d4ef296523529552f94a2ab1ee56113c1b990d7ae
$ docker ps --filter name=db
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
119ed37ba5c6 postgres:18 "docker-entrypoint.s…" 8 seconds ago Up 8 seconds 5432/tcp db
$ docker exec db psql -U postgres -c "CREATE TABLE notes (id serial, body text); INSERT INTO notes (body) VALUES ('hello volume');"
CREATE TABLE
INSERT 0 1
$ docker exec db psql -U postgres -c "SELECT * FROM notes;"
id | body
----+--------------
1 | hello volume
(1 row)
$ docker rm -f db
db
$ docker run -d --name db -e POSTGRES_PASSWORD=secret -v pgdata:/var/lib/postgresql postgres:18
d94fb2966b3dc5790239b4a48d180aedd193f6fc2c846d6878a7c5fcc450f7cd
$ docker exec db psql -U postgres -c "SELECT * FROM notes;"
id | body
----+--------------
1 | hello volume
(1 row)
新しいコンテナから、前のコンテナが書き込んだデータを読み取れました。データは Volume に保存されるため、コンテナのライフサイクルとは関係ありません。
PostgreSQL 18 のデータディレクトリ
古いチュートリアルでは
-v pgdata:/var/lib/postgresql/dataと書かれていることがよくあります。これは PostgreSQL 17 以前の書き方で、postgres:18では起動に失敗します。
$ docker run -d --name db -e POSTGRES_PASSWORD=secret -v pgdata:/var/lib/postgresql/data postgres:18
c1cec1074a4d440c1f6d106d994171d290cd8ea9da3266d798c0c81662bd8281
$ docker ps -a --filter name=db
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c1cec1074a4d postgres:18 "docker-entrypoint.s…" 8 seconds ago Exited (1) 7 seconds ago db
$ docker logs db
Error: in 18+, these Docker images are configured to store database data in a
format which is compatible with "pg_ctlcluster" (specifically, using
major-version-specific directory names). This better reflects how
PostgreSQL itself works, and how upgrades are to be performed.
See also https://github.com/docker-library/postgres/pull/1259
Counter to that, there appears to be PostgreSQL data in:
/var/lib/postgresql/data (unused mount/volume)
This is usually the result of upgrading the Docker image without
upgrading the underlying database using "pg_upgrade" (which requires both
versions).
The suggested container configuration for 18+ is to place a single mount
at /var/lib/postgresql which will then place PostgreSQL data in a
subdirectory, allowing usage of "pg_upgrade --link" without mount point
boundary issues.
See https://github.com/docker-library/postgres/issues/37 for a (long)
discussion around this process, and suggestions for how to do so.
$ docker rm -f db
db
$ docker volume rm pgdata
pgdata
PostgreSQL 18 の公式イメージでは、pg_upgrade で後からアップグレードできるように、データを /var/lib/postgresql/18/docker のような「メジャーバージョンごとに分かれた」サブディレクトリに保存するようになりました。新しい方法では、1つ上の階層である /var/lib/postgresql をマウントします。
$ docker volume ls
DRIVER VOLUME NAME
local 5775c1d90defc3dc23a92cdde10cd62b182d9c774ae43f0a40595fe62131a63c
local pgdata
$ docker volume inspect pgdata
[
{
"CreatedAt": "2026-10-13T00:09:44Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/pgdata/_data",
"Name": "pgdata",
"Options": null,
"Scope": "local"
}
]
$ docker exec db sh -c 'echo $PGDATA; ls /var/lib/postgresql'
/var/lib/postgresql/18/docker
18
PGDATAは現在/var/lib/postgresql/18/dockerです。docker volume inspectのMountpointは、Docker ホスト上にある Volume の実際の場所です。Docker Desktop では Docker の Linux 仮想マシン内にあるため、Windows / macOS のエクスプローラーから直接確認することはできません。バックアップには、以下で紹介する方法を使ってください。
Volume の管理
docker volume ls # すべての Volume を一覧表示
docker volume inspect pgdata # 詳細情報を確認
docker volume rm pgdata # 削除(コンテナで使用されていない場合のみ)
docker volume prune # 未使用の匿名 Volume を削除
docker volume prune -a # 名前付き Volume も削除(注意!)
docker volume prune -aとdocker system prune --volumesを実行すると、データベースのデータが削除されます。実行前に確認するか、先にバックアップを作成してください。
匿名 Volume はどこから来るのか
-v を指定していなくても、多くのデータベースイメージは Volume を作成します。Dockerfile に VOLUME 命令があるためです。
$ docker volume ls
DRIVER VOLUME NAME
$ docker run -d --name anon -e POSTGRES_PASSWORD=secret postgres:18
1db583d1b34d0482cfa458a5fe4a7fc7c1320633bde0339407a0c24fe62ad630
$ docker volume ls
DRIVER VOLUME NAME
local 0bbe1b19b9c02afcc2619b031ceddb1af1b4b75c3068f28eb64642973b3fd6e8
$ docker inspect --format '{{range .Mounts}}{{.Type}} {{.Name}} -> {{.Destination}}{{println}}{{end}}' anon
volume 0bbe1b19b9c02afcc2619b031ceddb1af1b4b75c3068f28eb64642973b3fd6e8 -> /var/lib/postgresql
$ docker rm -f anon
anon
$ docker volume ls
DRIVER VOLUME NAME
local 0bbe1b19b9c02afcc2619b031ceddb1af1b4b75c3068f28eb64642973b3fd6e8
$ docker volume prune -f
Deleted Volumes:
0bbe1b19b9c02afcc2619b031ceddb1af1b4b75c3068f28eb64642973b3fd6e8
Total reclaimed space: 40.49MB
docker runを実行しただけで、docker volume lsに長いランダムな名前の匿名 Volumeが1つ増え、/var/lib/postgresqlにマウントされます。docker rm -fでコンテナを削除しても、匿名 Volume は残ったままで、40 MB を占有します。Docker を使うほどディスク使用量が増えていく原因の1つは、忘れられた匿名 Volume です。docker volume pruneは、使われていない匿名 Volume を削除します。コンテナを削除するときに-v(docker rm -fv anon)を付けても、その匿名 Volume を一緒に削除できます。
用途がひと目でわかるよう、常に名前付き Volume を使うことをおすすめします。
Bind mount:ホストのフォルダーをコンテナにマウントする
docker run -d --name site -p 8080:80 -v "$(pwd)/site:/usr/share/nginx/html:ro" nginx:1.30
docker run -d --name site -p 8080:80 -v "${PWD}/site:/usr/share/nginx/html:ro" nginx:1.30
docker run -d --name site -p 8080:80 -v "%cd%/site:/usr/share/nginx/html:ro" nginx:1.30

ホスト上でエディターを使って site/index.html の見出しを変更して保存し、ブラウザーを再読み込みします。

これは開発で最もよく使われる方法です。コードはホスト上に置き、使い慣れたエディターで編集し、コンテナで実行します。
注意点:
- ホストのパスは絶対パスで指定します。
$(pwd)は現在のディレクトリに置き換わります(Windows PowerShell では${PWD}を使います)。 - パスではなく名前を指定した場合(例:
site:/data)、Docker は Volume 名として扱います。 - Linux では、コンテナが root として書き込んだファイルは、ホスト上でも root の所有になります。
-u $(id -u):$(id -g)を使うと、コンテナを自分のユーザーとして実行できます。 - 読み取り専用でマウントするには
:roを付けます。
-v と --mount で存在しないパスを指定した場合
$ ls -d nothere
ls: nothere: No such file or directory
$ docker run --rm -v "$(pwd)/nothere:/data" alpine:3.23 ls -la /data
total 8
drwxr-xr-x 2 root root 4096 Oct 13 00:09 .
drwxr-xr-x 1 root root 4096 Oct 13 00:09 ..
$ ls -ld nothere
drwxr-xr-x 2 root root 4096 Oct 13 00:09 nothere
$ docker run --rm --mount type=bind,src="$(pwd)/nothere2",dst=/data alpine:3.23 ls /data
docker: Error response from daemon: invalid mount config for type "bind": bind source path does not exist: /work/nothere2
Run 'docker run --help' for more information
Windows での書き方(タブをクリックして切り替えます):
docker run --rm -v "$(pwd)/nothere:/data" alpine:3.23 ls -la /data
docker run --rm --mount type=bind,src="$(pwd)/nothere2",dst=/data alpine:3.23 ls /data
docker run --rm -v "${PWD}/nothere:/data" alpine:3.23 ls -la /data
docker run --rm --mount type=bind,src="${PWD}/nothere2",dst=/data alpine:3.23 ls /data
docker run --rm -v "%cd%/nothere:/data" alpine:3.23 ls -la /data
docker run --rm --mount type=bind,src="%cd%/nothere2",dst=/data alpine:3.23 ls /data
-vでは、存在しないホストパスを指定すると空のフォルダーが自動的に作成されます(しかも root の所有になります)。コンテナは起動しますが、マウントされるのは空のフォルダーです。「マウントしたのにファイルがない」と戸惑う原因になりがちです。--mount type=bindでは、存在しないパスを指定するとエラーになります。パスの入力ミスに気付きやすくなります。
--mount の書き方は長くなりますが、より明確です。
docker run -d \
--mount type=volume,src=pgdata,dst=/var/lib/postgresql \
--mount type=bind,src="$(pwd)/init.sql",dst=/docker-entrypoint-initdb.d/init.sql,readonly \
-e POSTGRES_PASSWORD=secret postgres:18
docker run -d `
--mount type=volume,src=pgdata,dst=/var/lib/postgresql `
--mount type=bind,src="${PWD}/init.sql",dst=/docker-entrypoint-initdb.d/init.sql,readonly `
-e POSTGRES_PASSWORD=secret postgres:18
docker run -d ^
--mount type=volume,src=pgdata,dst=/var/lib/postgresql ^
--mount type=bind,src="%cd%/init.sql",dst=/docker-entrypoint-initdb.d/init.sql,readonly ^
-e POSTGRES_PASSWORD=secret postgres:18
tmpfs:メモリ上に保存する
$ docker run --rm --tmpfs /cache:size=64m alpine:3.23 df -h /cache
Filesystem Size Used Available Use% Mounted on
tmpfs 64.0M 0 64.0M 0% /cache
データはメモリ上にのみ保存され、コンテナを停止すると消えます。一時ファイルに適しています(Linux コンテナのみ)。
Volume のバックアップと復元
Volume の場所は Docker が管理します。最も汎用的なバックアップ方法は、一時コンテナを起動して Volume を tar ファイルにまとめることです。
$ docker run --rm -v pgdata:/data:ro -v "$(pwd):/backup" alpine:3.23 tar czf /backup/pgdata.tar.gz -C /data .
$ ls -lh pgdata.tar.gz
-rw-r--r-- 1 root root 4.6M Oct 13 00:10 pgdata.tar.gz
$ docker volume create pgdata-restore
pgdata-restore
$ docker run --rm -v pgdata-restore:/data -v "$(pwd):/backup" alpine:3.23 tar xzf /backup/pgdata.tar.gz -C /data
$ docker run --rm -v pgdata-restore:/data alpine:3.23 ls /data/18/docker
PG_VERSION
base
global
pg_commit_ts
pg_dynshmem
… (以下省略)
$ rm pgdata.tar.gz
Windows での書き方(タブをクリックして切り替えます):
docker run --rm -v pgdata:/data:ro -v "$(pwd):/backup" alpine:3.23 tar czf /backup/pgdata.tar.gz -C /data .
docker run --rm -v pgdata-restore:/data -v "$(pwd):/backup" alpine:3.23 tar xzf /backup/pgdata.tar.gz -C /data
docker run --rm -v pgdata:/data:ro -v "${PWD}:/backup" alpine:3.23 tar czf /backup/pgdata.tar.gz -C /data .
docker run --rm -v pgdata-restore:/data -v "${PWD}:/backup" alpine:3.23 tar xzf /backup/pgdata.tar.gz -C /data
docker run --rm -v pgdata:/data:ro -v "%cd%:/backup" alpine:3.23 tar czf /backup/pgdata.tar.gz -C /data .
docker run --rm -v pgdata-restore:/data -v "%cd%:/backup" alpine:3.23 tar xzf /backup/pgdata.tar.gz -C /data
- 1つ目のコマンドでは、
pgdataを読み取り専用で/dataに、現在のフォルダーを/backupにマウントし、tar で/dataをpgdata.tar.gzにまとめてホストの現在のフォルダーに保存します。 - 復元時は新しい Volume を作成し、同じ方法でその中に解凍します。復元した Volume では、
18/dockerの下に PostgreSQL のデータファイルがあることを確認できます。
稼働中のデータベースのファイルを直接アーカイブすると、データに不整合が生じることがあります。上の例では、データベースを停止してからバックアップしています。データベースには、
pg_dumpやmysqldumpなどの専用エクスポートツールを使うことをおすすめします。詳しくはDocker で PostgreSQL を実行するを参照してください。
どれを使えばよいですか?
- データベースなど、長期保存が必要なデータ → Volume
- 開発中にコードの変更をすぐ確認したい → Bind mount
- ホストの設定ファイルをコンテナに読み込ませる → Bind mount(読み取り専用)
- 一時データで、保存する必要がない → tmpfs またはコンテナ内に直接書き込む
まとめ
- データベースなど、保存が必要なデータには名前付き Volume を使います。
- 開発時は Bind mount を使い、コンテナからホスト上のコードを直接利用します。
-vは存在しないホストパスを空のフォルダーとして作成します。--mountはエラーを返します。- Volume をバックアップするには、一時コンテナに Volume とホストのフォルダーを同時にマウントし、tar でアーカイブします。
練習問題
練習 1:Volume notes を作成し、コンテナからファイルを書き込みます。コンテナを削除した後、別のコンテナからそのファイルを読み取ってください。
解答
docker volume create notes
docker run --rm -v notes:/data alpine:3.23 sh -c "echo hello > /data/a.txt"
docker run --rm -v notes:/data alpine:3.23 cat /data/a.txt
hello と出力されます。Volume の管理コマンドについては docker volume を参照してください。
練習 2:docker run -v data:/app と docker run -v ./data:/app の違いは何ですか?
解答
data にはスラッシュが含まれていないため、Volume 名として扱われます。./data はパスなので Bind mount となり、ホストの現在のフォルダー内にある data をマウントします。
よくある質問
Volume のデータはコンピューターのどこに保存されますか?
Linux では /var/lib/docker/volumes/名前/_data にあります。Docker Desktop の Volume は Linux 仮想マシン内にあるため、Windows のエクスプローラーや Finder からは見えません。Docker Desktop の Volumes 画面で確認できます。
Windows で -v のホストパスはどう書きますか?
PowerShell では "${PWD}/site:/app"、CMD では "%cd%/site:/app" を使います。この章のコマンドには、3種類の書き方を切り替えるタブがあります。
Bind mount のファイル権限がおかしい場合は?
Linux では、コンテナが root として書き込んだファイルは、ホスト上でも root の所有になります。-u $(id -u):$(id -g) を使うと、コンテナを自分のユーザーとして実行できます。
更新日 2026年10月14日