ปัญหาที่เจอ
- Container ตาย → ข้อมูลใน DB หาย
- Mount โฟลเดอร์จาก host → permission denied
- File เขียนใน container แต่ host ไม่เห็น (หรือกลับกัน)
- Write ใน volume ช้ากว่า write ใน container ตรงๆ
ปัญหาเหล่านี้มาจากการเข้าใจ "storage type" ของ Docker ไม่ครบ
3 ประเภท Storage ใน Docker
┌─────────────────────────────────────┐
│ Container (writable layer) │ ← หายเมื่อลบ container
└─────────────────────────────────────┘
│ │ │
▼ ▼ ▼
[bind] [volume] [tmpfs]
folder /var/... RAM
host /docker/
1. Container Writable Layer (default)
ทุก container มี layer เขียนได้ของตัวเอง — เก็บใน /var/lib/docker/overlay2/.../diff/
ทุกอย่างที่ container เขียนโดยไม่ mount = อยู่ใน layer นี้
ปัญหา:
- ลบ container → ข้อมูลหาย
- Performance ช้ากว่า volume (overlay filesystem)
- ขนาดใหญ่ขึ้นเรื่อยๆ ทำ disk เต็ม
ใช้กับ: ไฟล์ชั่วคราว, build artifact ที่ไม่สำคัญ
2. Named Volume
Docker จัดการที่อยู่ + lifecycle ให้
services:
db:
image: postgres:16-alpine
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
หรือ command line:
docker volume create pgdata
docker run -v pgdata:/var/lib/postgresql/data postgres
ที่อยู่จริง: /var/lib/docker/volumes/pgdata/_data/
ข้อดี:
- คงอยู่หลังลบ container
- Permission/ownership Docker จัดการ
- Performance เท่า host filesystem
- Backup/migrate ง่าย
ใช้กับ: data ของ database, cache, app data — ใช้เป็นหลัก
3. Bind Mount
Mount path ของ host ตรงๆ:
services:
app:
volumes:
- ./src:/usr/src/app
- /var/log/myapp:/var/log/myapp
ข้อดี:
- Host เห็นไฟล์ที่ container เขียน (ดีกับ dev — hot reload)
- Path control ได้เอง
ข้อเสีย:
- Permission ระหว่าง host user กับ container user ต้องตรงกัน — ปัญหาที่เจอบ่อยสุด
- Performance ลดบน macOS / Windows (ผ่าน VM)
- Path hardcode — ย้าย server ลำบาก
ใช้กับ: dev environment (source code), config file, log directory ที่ต้องการ access จาก host
4. tmpfs (เก็บใน RAM)
services:
app:
tmpfs:
- /tmp
- /run:size=64m
ข้อดี:
- เร็วที่สุด (RAM)
- Container ตาย ข้อมูลหายแน่นอน
ใช้กับ: secret ชั่วคราว, cache ที่ไม่จำเป็นต้อง persist
ปัญหาที่เจอบ่อย (พร้อมวิธีแก้)
Permission denied ใน bind mount
$ docker run -v $(pwd):/app node npm install
EACCES: permission denied, mkdir '/app/node_modules'
สาเหตุ: container รันด้วย user node (UID 1000) — แต่ host folder owned by user อื่น
วิธี 1: รันด้วย user ของ host
docker run -v $(pwd):/app -u $(id -u):$(id -g) node npm install
วิธี 2: chown ใน Dockerfile
FROM node:20-alpine
RUN addgroup -g 1000 -S app && adduser -u 1000 -S app -G app
USER app
วิธี 3: ใช้ named volume แทน bind mount
services:
app:
volumes:
- ./src:/app/src # source code (read-only ก็ได้)
- node_modules:/app/node_modules # Docker จัดการ permission
volumes:
node_modules:
node_modules หายเมื่อ bind mount
services:
app:
volumes:
- .:/app # bind mount ทุกอย่าง รวม node_modules ของ host (อาจไม่มี)
docker run -v $(pwd):/app ทับ node_modules ใน image
แก้: mask node_modules ด้วย named volume:
volumes:
- .:/app
- /app/node_modules # anonymous volume mask folder นี้
Docker จะใช้ /app/node_modules ที่อยู่ใน image แทน folder จาก host
Database data หายหลัง down -v
docker compose down -v
-v = ลบ volume ด้วย → data ใน Postgres หาย
อย่าใช้ -v ใน production — ใช้:
docker compose down # ไม่ลบ volume
Write ช้าบน macOS / Windows
bind mount ผ่าน VM (HyperKit / WSL) → write ผ่าน 2 layer ช้า 5-10 เท่า
แก้:
- ใช้ named volume สำหรับ
node_modules, build cache - ใช้ Docker Desktop option
VirtioFS(macOS) — เร็วขึ้น 2-3 เท่า - บน Windows ใช้ WSL2 + เก็บ source ใน
/home/...ไม่ใช่/mnt/c/...
Backup volume
# backup volume เป็น tar
docker run --rm \
-v pgdata:/data \
-v $(pwd):/backup \
busybox tar czf /backup/pgdata-$(date +%F).tar.gz -C /data .
# restore
docker run --rm \
-v pgdata:/data \
-v $(pwd):/backup \
busybox tar xzf /backup/pgdata-2026-01-15.tar.gz -C /data
หรือใช้ tool เฉพาะ database — pg_dump ดีกว่า raw filesystem backup
ดูว่า volume กิน disk เท่าไหร่
docker system df -v | grep -A 50 "Local Volumes"
หรือ:
sudo du -sh /var/lib/docker/volumes/*/ | sort -hr | head
Inspect ว่า volume อยู่ไหน
docker volume inspect pgdata
[
{
"Name": "pgdata",
"Mountpoint": "/var/lib/docker/volumes/pgdata/_data",
"Driver": "local",
...
}
]
เข้าไปดูตรงๆ:
sudo ls -la /var/lib/docker/volumes/pgdata/_data/
Volume Driver — เกินกว่า local
Docker รองรับ volume driver อื่น:
nfs— mount NFS sharecifs— mount Windows sharecloudstor— AWS EFS, GCP Filestorerexray— multi-cloud volumes
ตัวอย่าง NFS:
volumes:
shared:
driver: local
driver_opts:
type: nfs
o: addr=10.0.0.5,nfsvers=4
device: ":/exported/path"
Volume vs Bind Mount — เลือกยังไง
| Use Case | เลือก |
|---|---|
| Database data | Named volume |
| Cache (Redis) | Named volume |
| App data ที่ต้อง persist | Named volume |
| Source code (dev) | Bind mount |
| Config file (/etc/app/config.yml) | Bind mount read-only |
| Log directory | Bind mount หรือ centralized log |
| Secret ชั่วคราว | tmpfs |
| /tmp, /run | tmpfs |
Best Practice
1. ใช้ named volume สำหรับ data
ไม่ bind mount /var/lib/postgresql/data จาก host:
# ❌
volumes:
- /opt/pgdata:/var/lib/postgresql/data
# ✅
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
2. Read-only ถ้าไม่ต้องเขียน
volumes:
- ./config:/etc/app:ro
ป้องกัน container แก้ config ของ host
3. แยก dev compose กับ prod
docker-compose.override.yml (dev — auto-load):
services:
app:
volumes:
- ./src:/app/src # bind mount เพื่อ hot reload
docker-compose.prod.yml:
services:
app:
image: ghcr.io/me/myapp:1.0 # ใช้ image ที่ build มาแล้ว — ไม่ต้อง bind mount
4. Label volume ให้ดู
volumes:
pgdata:
labels:
project: "myapp"
environment: "production"
backup: "daily"
ดูเพื่อตอน:
docker volume ls --filter label=project=myapp
Cheat Sheet
# list volume
docker volume ls
# create
docker volume create myvol
# inspect (ดูตำแหน่งจริง)
docker volume inspect myvol
# ลบ volume เดี่ยว
docker volume rm myvol
# ลบ volume ที่ไม่มี container ใช้
docker volume prune
# ลบ volume ของ project compose ทั้งหมด (ระวัง!)
docker compose down -v
# backup
docker run --rm -v myvol:/data -v $(pwd):/backup busybox \
tar czf /backup/myvol.tar.gz -C /data .
สรุป
- Container layer = ชั่วคราว, ลบ container แล้วหาย
- Named volume = ที่เก็บข้อมูลหลักของ production
- Bind mount = dev / config / share file ระหว่าง host กับ container
- tmpfs = RAM, secret ชั่วคราว
ปัญหา 90%:
- Permission ใน bind mount → ใช้
-uflag หรือ named volume - Data หาย → อย่า
down -v, ใช้ named volume - Performance ช้า → ใช้ named volume (ไม่ใช่ bind) สำหรับ heavy I/O
อ่านต่อ: Docker Disk Usage — ตรวจขนาด volume