ข้ามไปยังเนื้อหา

Named Volumes

วัดบน Docker Desktop 4.81.0 / Engine 29.6.1 (macOS, linux/arm64), 2026-09-30

Named volume คือหน่วยเก็บข้อมูลที่ Docker สร้างและจัดการให้คุณ Docker เก็บข้อมูล volume ไว้ในไดเรกทอรีของตัวเอง (โดยค่าเริ่มต้นคือ /var/lib/docker/volumes/ บนเครื่องที่รัน engine) คุณอ้างถึง volume ด้วยชื่อ — ไม่ใช่ host path

เพราะ Docker เป็นเจ้าของตำแหน่งจัดเก็บ named volume จึง:

  • ทำงานแบบเดียวกันบนทุก OS ที่ engine รันอยู่ (คุณส่งชื่อ ไม่ใช่ path)
  • รอดจาก docker rm — volume แยกจาก container
  • แชร์ระหว่างหลาย container พร้อมกันได้
  • เป็นวิธีที่แนะนำสำหรับเก็บข้อมูลของ application (Docker docs: volumes)
Terminal window
docker volume create mydata
docker volume ls
docker volume inspect mydata

Output จริง (volume ของคอร์สชื่อ docker-b2-mydata):

$ docker volume ls
DRIVER VOLUME NAME
local docker-b2-mydata
$ docker volume inspect docker-b2-mydata
[
{
"CreatedAt": "2026-09-30T16:29:16Z",
"Driver": "local",
"Labels": {
"course-agent": "docker-b2"
},
"Mountpoint": "/var/lib/docker/volumes/docker-b2-mydata/_data",
"Name": "docker-b2-mydata",
"Options": null,
"Scope": "local"
}
]
$ docker volume rm docker-b2-nonexist
Error response from daemon: get docker-b2-nonexist: no such volume
$ docker volume rm docker-b2-busy # a running container mounts it
Error response from daemon: remove docker-b2-busy: volume is in use - [b3b9cd6c...]

ลบ volume ไม่ได้ตราบใดที่ container ใดๆ (แม้ตัวที่หยุดแล้ว) ยังอ้างถึงมัน ให้ลบ container ก่อน

มีสอง syntax ทั้งคู่สร้าง mount แบบเดียวกัน

Syntax สั้น -v:

Terminal window
docker run -d --name myapp -v mydata:/app/data myimage

Syntax ยาว --mount (ชัดเจนและแนะนำสำหรับ script):

Terminal window
docker run -d --name myapp \
--mount type=volume,source=mydata,target=/app/data \
myimage

ถ้า volume mydata ยังไม่มี Docker จะสร้างให้อัตโนมัติเมื่อคุณรัน container

เมื่อ mount volume ว่าง ลงบนไดเรกทอรีที่มีเนื้อหาอยู่แล้วใน image Docker จะคัดลอกเนื้อหานั้นเข้า volume ก่อน bind mount ไม่ทำแบบนี้

$ docker volume create docker-b2-html
$ docker run --rm -v docker-b2-html:/usr/share/nginx/html nginx:1.31-alpine ls -l /usr/share/nginx/html
-rw-r--r-- 1 root root 497 Sep 15 14:18 50x.html
-rw-r--r-- 1 root root 896 Sep 15 14:18 index.html
$ docker run --rm -v docker-b2-html:/data alpine:3.24 ls /data # a different image, same volume
50x.html
index.html

ตอนนี้ volume เก็บหน้าเริ่มต้นของ nginx ทั้งที่ image ของ container ที่สองไม่มี ถ้า volume ไม่ว่าง เนื้อหาของ volume จะชนะ และไฟล์ของ image ที่ path นั้นถูกซ่อน

-v /data (ไม่มีชื่อ) หรือคำสั่ง VOLUME ใน image สร้าง volume แบบ anonymous ด้วยชื่อสุ่ม 64 ตัวอักษร และ label com.docker.volume.anonymous:

$ docker run -d --name anon -v /data alpine:3.24 sleep 60
$ docker inspect -f '{{range .Mounts}}{{.Type}} name={{.Name}} dst={{.Destination}}{{end}}' anon
volume name=886e1acf723f1af30efc071d3c179610f9f7eb902330b289d760d4dbcb9eb3f2 dst=/data

Anonymous volume ตามยากว่ามีอะไรค้างอยู่ นั่นเป็นเหตุผลที่ image อย่าง postgres (ประกาศ VOLUME) ทิ้งหนึ่ง volume ไว้ทุกครั้งที่คุณ docker run โดยไม่ตั้งชื่อ mount

ทั้งสองคำสั่งแคบกว่าที่คนส่วนใหญ่คาด:

# rm -v removes anonymous volumes only: the named volume survives
$ docker rm -f -v named # container mounted docker-b2-mydata:/data
$ docker volume ls --filter label=course-agent=docker-b2 -q
docker-b2-html
docker-b2-mydata
# prune without -a removes only unreferenced ANONYMOUS volumes
$ docker volume prune -f --filter label=course-agent=docker-b2
Deleted Volumes:
405eac1fcb1292d90e253b04262d4fb3a18957f074ecae35d5c8600bb9607de7 # the anonymous one
$ docker volume ls --filter label=course-agent=docker-b2
DRIVER VOLUME NAME
local docker-b2-html
local docker-b2-mydata
# -a also removes unreferenced NAMED volumes
$ docker volume prune -f -a --filter label=course-agent=docker-b2
Deleted Volumes:
docker-b2-mydata
docker-b2-html
  • docker rm -v ลบ volume anonymous ของ container; named volume ยังอยู่ (พิสูจน์แล้ว: docker-b2-* ทั้งสองยังอยู่ในรายการ)
  • docker volume prune (Engine 29) ลบ anonymous volume ที่ไม่ได้ใช้; เพิ่ม -a / --all เพื่อลบ named volume ที่ไม่ได้ใช้ด้วย (reference)
  • --filter label=… จำกัดทุก prune ข้างต้นให้อยู่ในวัตถุของบทเรียนนี้ VM ของ Docker Desktop ในการทดสอบนี้มี 77 volumes จากโปรเจกต์อื่น -a ที่ไม่มี filter จะลบทุกตัวที่ไม่มีใครอ้างถึง ถือว่า -a เป็นคำสั่งทำลายล้าง

คุณสมบัติสำคัญของ named volume: ลบ container แล้วสร้างใหม่ได้ ข้อมูลทั้งหมดที่เขียนลง volume ยังอยู่

Terminal window
# Run a container and write data
docker run --name writer -v mydata:/app/data alpine:3.24 sh -c "echo 'persistent' > /app/data/file.txt"
# Remove the container
docker rm writer
# Start a brand-new container mounting the same volume
docker run --rm -v mydata:/app/data alpine:3.24 cat /app/data/file.txt
# Output: persistent

Snippet ด้านล่างรัน container เขียนข้อมูลลง named volume ลบ container แล้ว mount volume เดิมใน container ใหม่เพื่อพิสูจน์ว่าข้อมูลรอด

# 1. Create a named volume
docker volume create demo-vol

# 2. Write data into the volume via a container
docker run --rm -v demo-vol:/data alpine:3.24 sh -c "echo 'data survives!' > /data/message.txt"

# 3. Read it back from a completely new container
docker run --rm -v demo-vol:/data alpine:3.24 cat /data/message.txt

# 4. Inspect the volume
docker volume inspect demo-vol

# 5. Clean up
docker volume rm demo-vol

Compose file สำหรับฐานข้อมูล Postgres 18 ที่ AI เขียน โดยคัดมาจากบทช่วยสอนรุ่นเก่า:

🐛 หา bug ในโค้ดนี้

compose.yaml
services:
db:
image: postgres:18-alpine
environment:
POSTGRES_PASSWORD: <your-db-password>
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
เฉลย

container ไม่เคย healthy มันออกทันทีด้วย exit code 1 พร้อมคำอธิบายยาว:

$ docker ps -a --filter name=docker-b2-pg --format '{{.Status}}'
Exited (1) 4 seconds ago
$ docker logs docker-b2-pg
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). ...
Counter to that, there appears to be PostgreSQL data in:
/var/lib/postgresql/data (unused mount/volume)
...
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 ...

สาเหตุอยู่ใน image: docker image inspect postgres:18-alpine แสดง Volumes: {"/var/lib/postgresql":{}} และ PGDATA=/var/lib/postgresql/18/docker จนถึง Postgres 17 data directory คือ /var/lib/postgresql/data ตั้งแต่ 18 image ประกาศ ไดเรกทอรีแม่ เป็น volume mount point แบบเก่าวาง named volume ในที่ที่ image ไม่มองอีกแล้ว และ Docker ยังแนบ anonymous volume ใหม่ที่ /var/lib/postgresql ด้วย (docker inspect แสดงทั้งสอง mount) ให้ mount volume ที่ /var/lib/postgresql แทน:

compose.yaml
services:
db:
image: postgres:18-alpine
environment:
POSTGRES_PASSWORD: <your-db-password>
volumes:
- pgdata:/var/lib/postgresql
volumes:
pgdata:

ด้วยไฟล์นี้ docker compose up -d --wait รายงาน Healthy, show data_directory คืน /var/lib/postgresql/18/docker และ volume มีไดเรกทอรี 18/docker ให้อ่าน mount point ที่ image ระบุไว้เสมอ (hub.docker.com/_/postgres) และตรวจ docker image inspect --format '{{json .Config.Volumes}}' เมื่ออัปเกรด major version

Named volume คือ พื้นที่จัดเก็บที่มีชื่อและอายุของตัวเอง container อ้างถึงมัน การอ้างถึงนั้นคือสิ่งที่ทำให้ volume rm ล้มเหลวและสิ่งที่ prune มองหา การ mount กำหนดว่าพื้นที่ปรากฏ ตรงไหนใน container ดังนั้น path ต้องตรงกับที่โปรแกรมเขียนจริง — image ที่ย้าย data directory (Postgres 18) เปลี่ยนโดยไม่บอกว่า mount ไหนสำคัญ

ตัวเลือกข้อดีข้อเสีย
Named volumeDocker จัดการ path พกพาข้าม host ได้ สำรองง่ายด้วย throwaway container copy-up เติมข้อมูลเริ่มต้นให้เข้าถึงข้อมูลจาก filesystem ของ host ตรงๆ ไม่ได้ (บน Desktop อยู่ใน VM) — ต้องผ่าน container เสมอ
Anonymous volumeไม่ต้องตั้งค่า VOLUME ใน image ให้การเก็บถาวรโดยอัตโนมัติชื่อสุ่ม ตกค้างง่าย rm -v/prune ลบมัน ข้อมูล “ถาวร” จึงอาจหาย
Bind mountเห็นและแก้ไฟล์จาก host ได้ทันทีผูกกับ host path เฉพาะ ย้ายระหว่างสภาพแวดล้อมยาก
  • ใช้ bind mount แทน named volume กับข้อมูลฐานข้อมูล production ทำให้เสียความสามารถในการพกพา
  • รัน docker compose down -v โดยไม่ตั้งใจแล้วเสียข้อมูลทั้งหมดใน volume
  • ไม่วางแผน backup ของ volume โดยคิดว่า “volume อยู่ตลอดไป”
  • รัน docker volume prune -a หรือ docker system prune -a --volumes บน host ที่ใช้ร่วมกันโดยไม่ตรวจว่ามีอะไรใช้อยู่
  • mount volume ของฐานข้อมูลที่ path ซึ่ง major version เก่าใช้
  • “docker rm -v ลบ volume ของ container” เฉพาะ anonymous volume; named volume รอดเสมอ
  • “docker volume prune ลบ volume ที่ไม่ได้ใช้ทั้งหมด” บน Engine 29 ลบเฉพาะ anonymous volume ที่ไม่ได้ใช้; named volume ที่ไม่ได้ใช้ต้องมี -a
  • “Mountpoint คือโฟลเดอร์บน Mac ของฉัน” บน Docker Desktop มันอยู่ใน VM
  • “การ mount volume ซ่อนไฟล์ของ image เสมอ” เฉพาะเมื่อ volume ไม่ว่าง named volume ที่ว่างจะถูกเติมจาก image ก่อน (copy-up)
เกิดอะไรขึ้นกับ named volume เมื่อคุณรัน docker rm กับ container ที่ใช้มัน?
flag ใด mount named volume ด้วย syntax แบบสั้น?
docker volume prune -f (ไม่มี -a) ลบอะไรบน Engine 29?
ถ้าคุณระบุชื่อ volume ที่ยังไม่มีในคำสั่ง docker run Docker ทำอะไร?
คุณ mount named volume ที่ว่างลงที่ /usr/share/nginx/html ของ image nginx หลังจากนั้น volume มีอะไรอยู่?
postgres:18-alpine กับ -v pgdata:/var/lib/postgresql/data ออกด้วย code 1 วิธีแก้คืออะไร?