Named Volumes
วัดบน Docker Desktop 4.81.0 / Engine 29.6.1 (macOS, linux/arm64), 2026-09-30
Named volume คืออะไร?
หัวข้อที่มีชื่อว่า “Named volume คืออะไร?”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)
การจัดการ volumes
หัวข้อที่มีชื่อว่า “การจัดการ volumes”สร้าง แสดงรายการ และ inspect
หัวข้อที่มีชื่อว่า “สร้าง แสดงรายการ และ inspect”docker volume create mydatadocker volume lsdocker volume inspect mydataOutput จริง (volume ของคอร์สชื่อ docker-b2-mydata):
$ docker volume lsDRIVER VOLUME NAMElocal 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" }]ลบ volume
หัวข้อที่มีชื่อว่า “ลบ volume”$ docker volume rm docker-b2-nonexistError response from daemon: get docker-b2-nonexist: no such volume$ docker volume rm docker-b2-busy # a running container mounts itError response from daemon: remove docker-b2-busy: volume is in use - [b3b9cd6c...]ลบ volume ไม่ได้ตราบใดที่ container ใดๆ (แม้ตัวที่หยุดแล้ว) ยังอ้างถึงมัน ให้ลบ container ก่อน
การ mount volume เข้า container
หัวข้อที่มีชื่อว่า “การ mount volume เข้า container”มีสอง syntax ทั้งคู่สร้าง mount แบบเดียวกัน
Syntax สั้น -v:
docker run -d --name myapp -v mydata:/app/data myimageSyntax ยาว --mount (ชัดเจนและแนะนำสำหรับ script):
docker run -d --name myapp \ --mount type=volume,source=mydata,target=/app/data \ myimageถ้า volume mydata ยังไม่มี Docker จะสร้างให้อัตโนมัติเมื่อคุณรัน container
Copy-up: volume ว่างรับไฟล์จาก image
หัวข้อที่มีชื่อว่า “Copy-up: volume ว่างรับไฟล์จาก image”เมื่อ 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 volume50x.htmlindex.htmlตอนนี้ volume เก็บหน้าเริ่มต้นของ nginx ทั้งที่ image ของ container ที่สองไม่มี ถ้า volume ไม่ว่าง เนื้อหาของ volume จะชนะ และไฟล์ของ image ที่ path นั้นถูกซ่อน
Anonymous volumes
หัวข้อที่มีชื่อว่า “Anonymous volumes”-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}}' anonvolume name=886e1acf723f1af30efc071d3c179610f9f7eb902330b289d760d4dbcb9eb3f2 dst=/dataAnonymous volume ตามยากว่ามีอะไรค้างอยู่ นั่นเป็นเหตุผลที่ image อย่าง postgres (ประกาศ VOLUME) ทิ้งหนึ่ง volume ไว้ทุกครั้งที่คุณ docker run โดยไม่ตั้งชื่อ mount
docker rm -v และ docker volume prune ลบอะไรจริงๆ
หัวข้อที่มีชื่อว่า “docker rm -v และ docker volume prune ลบอะไรจริงๆ”ทั้งสองคำสั่งแคบกว่าที่คนส่วนใหญ่คาด:
# 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 -qdocker-b2-htmldocker-b2-mydata
# prune without -a removes only unreferenced ANONYMOUS volumes$ docker volume prune -f --filter label=course-agent=docker-b2Deleted Volumes:405eac1fcb1292d90e253b04262d4fb3a18957f074ecae35d5c8600bb9607de7 # the anonymous one$ docker volume ls --filter label=course-agent=docker-b2DRIVER VOLUME NAMElocal docker-b2-htmllocal docker-b2-mydata
# -a also removes unreferenced NAMED volumes$ docker volume prune -f -a --filter label=course-agent=docker-b2Deleted Volumes:docker-b2-mydatadocker-b2-htmldocker 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เป็นคำสั่งทำลายล้าง
ข้อมูลคงอยู่เมื่อเปลี่ยน container
หัวข้อที่มีชื่อว่า “ข้อมูลคงอยู่เมื่อเปลี่ยน container”คุณสมบัติสำคัญของ named volume: ลบ container แล้วสร้างใหม่ได้ ข้อมูลทั้งหมดที่เขียนลง volume ยังอยู่
# Run a container and write datadocker run --name writer -v mydata:/app/data alpine:3.24 sh -c "echo 'persistent' > /app/data/file.txt"
# Remove the containerdocker rm writer
# Start a brand-new container mounting the same volumedocker 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-volReading AI-generated code
หัวข้อที่มีชื่อว่า “Reading AI-generated code”Compose file สำหรับฐานข้อมูล Postgres 18 ที่ AI เขียน โดยคัดมาจากบทช่วยสอนรุ่นเก่า:
🐛 หา bug ในโค้ดนี้
services:db: image: postgres:18-alpine environment: POSTGRES_PASSWORD: <your-db-password> volumes: - pgdata:/var/lib/postgresql/datavolumes: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-pgError: 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 แทน:
services: db: image: postgres:18-alpine environment: POSTGRES_PASSWORD: <your-db-password> volumes: - pgdata:/var/lib/postgresqlvolumes: 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
Mental Model
หัวข้อที่มีชื่อว่า “Mental Model”Named volume คือ พื้นที่จัดเก็บที่มีชื่อและอายุของตัวเอง container อ้างถึงมัน การอ้างถึงนั้นคือสิ่งที่ทำให้ volume rm ล้มเหลวและสิ่งที่ prune มองหา การ mount กำหนดว่าพื้นที่ปรากฏ ตรงไหนใน container ดังนั้น path ต้องตรงกับที่โปรแกรมเขียนจริง — image ที่ย้าย data directory (Postgres 18) เปลี่ยนโดยไม่บอกว่า mount ไหนสำคัญ
Trade-offs
หัวข้อที่มีชื่อว่า “Trade-offs”| ตัวเลือก | ข้อดี | ข้อเสีย |
|---|---|---|
| Named volume | Docker จัดการ path พกพาข้าม host ได้ สำรองง่ายด้วย throwaway container copy-up เติมข้อมูลเริ่มต้นให้ | เข้าถึงข้อมูลจาก filesystem ของ host ตรงๆ ไม่ได้ (บน Desktop อยู่ใน VM) — ต้องผ่าน container เสมอ |
| Anonymous volume | ไม่ต้องตั้งค่า VOLUME ใน image ให้การเก็บถาวรโดยอัตโนมัติ | ชื่อสุ่ม ตกค้างง่าย rm -v/prune ลบมัน ข้อมูล “ถาวร” จึงอาจหาย |
| Bind mount | เห็นและแก้ไฟล์จาก host ได้ทันที | ผูกกับ host path เฉพาะ ย้ายระหว่างสภาพแวดล้อมยาก |
Common mistakes
หัวข้อที่มีชื่อว่า “Common mistakes”- ใช้ 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 เก่าใช้
Common Misconceptions
หัวข้อที่มีชื่อว่า “Common Misconceptions”- “
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)