September 27, 2026
Docker Volume.
Memahami named volume, bind mount, dan tmpfs di Docker: kapan memakai masing-masing, cara backup & restore volume, serta jebakan permission/UID.
Container itu sementara: begitu container dihapus, semua file yang ditulis di dalam layer-nya ikut hilang. Supaya data seperti database, upload user, atau cache tetap ada, Docker menyediakan tiga jenis mount: named volume, bind mount, dan tmpfs. Catatan ini merangkum bedanya, cara backup/restore, dan masalah permission yang paling sering muncul.
1. Tiga Jenis Mount
| Jenis | Data disimpan di | Dikelola oleh | Cocok untuk |
|---|---|---|---|
| Named volume | Area Docker (/var/lib/docker/volumes/... di Linux, di dalam VM di macOS/Windows) | Docker | Database, data persisten, vendor//node_modules/ saat dev |
| Bind mount | Path apa pun di host (./src, /etc/nginx/...) | Kamu | Source code saat development, file config |
| tmpfs | RAM (hilang saat container stop) | Kernel | Data sementara/sensitif, cache cepat |
Aturan praktis: data milik aplikasi β named volume, file milik developer β bind mount, data yang tidak boleh menyentuh disk β tmpfs.
2. Named Volume
docker volume create pgdata
docker volume ls
docker volume inspect pgdata
docker run -d --name db \
-e POSTGRES_PASSWORD=secret \
-v pgdata:/var/lib/postgresql/data \
postgres:17
Sintaks --mount lebih eksplisit (disarankan di dokumentasi resmi karena lebih jelas dibaca):
docker run -d --name db \
--mount type=volume,source=pgdata,target=/var/lib/postgresql/data \
postgres:17
Perilaku penting:
- Jika volume kosong dan path target di image sudah berisi file, Docker menyalin isi image ke volume saat pertama kali dipakai. Bind mount tidak melakukan ini β ia langsung menutupi isi folder.
- Volume tetap ada walau container dihapus (
docker rm). Ia hanya hilang lewatdocker volume rm,docker volume prune, ataudocker compose down -v.
Di Compose :
services:
mysql:
image: mysql:8.4
volumes:
- mysql-data:/var/lib/mysql
volumes:
mysql-data: # dibuat otomatis dengan nama <project>_mysql-data
shared-uploads:
external: true # volume sudah dibuat di luar Compose, jangan dibuat ulang
3. Bind Mount
docker run --rm -it \
-v "$(pwd)":/app \
-w /app \
node:22-alpine npm test
Versi --mount (akan error kalau path host tidak ada β lebih aman daripada -v yang diam-diam membuat folder kosong):
docker run --rm --mount type=bind,source="$(pwd)",target=/app,readonly node:22-alpine ls /app
Tambahkan :ro / readonly untuk file config yang tidak boleh diubah container:
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
4. tmpfs
docker run -d --tmpfs /app/cache:rw,size=64m myapp
services:
app:
tmpfs:
- /tmp
tmpfs hanya tersedia untuk container Linux. Isinya hilang begitu container berhenti, jadi jangan simpan apa pun yang perlu dipertahankan.
5. Backup & Restore Volume
Cara paling portabel: jalankan container sementara yang me-mount volume, lalu tar isinya ke folder host.
Backup
docker run --rm \
-v mysql-data:/data:ro \
-v "$(pwd)":/backup \
alpine tar czf /backup/mysql-data-$(date +%F).tar.gz -C /data .
Restore ke volume baru
docker volume create mysql-data-restore
docker run --rm \
-v mysql-data-restore:/data \
-v "$(pwd)":/backup \
alpine sh -c "tar xzf /backup/mysql-data-2026-09-27.tar.gz -C /data"
Khusus database: pakai dump, bukan salin file
Menyalin file mentah database yang sedang berjalan bisa menghasilkan backup korup. Hentikan dulu containernya (docker compose stop mysql) sebelum tar, atau lebih baik gunakan tool dump bawaan:
# MySQL
docker compose exec mysql sh -c 'mysqldump -u root -p"$MYSQL_ROOT_PASSWORD" --single-transaction app' > app.sql
docker compose exec -T mysql sh -c 'mysql -u root -p"$MYSQL_ROOT_PASSWORD" app' < app.sql
# PostgreSQL
docker compose exec -T db pg_dump -U postgres app > app.sql
docker compose exec -T db psql -U postgres app < app.sql
Flag -T mematikan alokasi TTY sehingga redirect < / > berjalan benar.
6. Permission & UID: Sumber Masalah Nomor Satu
Linux tidak peduli nama user, hanya UID/GID angka. Proses di container sering berjalan sebagai root (UID 0) atau user khusus seperti www-data (UID 33) / node (UID 1000). Saat menulis ke bind mount, file di host ikut memiliki UID tersebut.
Gejala umum:
- Setelah
composer installdi container, foldervendor/di host milikrootβ tidak bisa dihapus tanpasudo. - Laravel error
The stream or file "storage/logs/laravel.log" could not be opened: Permission denied. - Postgres/MySQL gagal start karena folder data di bind mount tidak bisa ditulis.
Solusi dari yang paling sederhana:
1. Jalankan container sebagai UID kamu
docker run --rm -u "$(id -u):$(id -g)" -v "$(pwd)":/app -w /app composer install
services:
app:
user: "${UID:-1000}:${GID:-1000}"
2. Samakan UID user di image dengan host (lewat build arg)
FROM php:8.4-fpm
ARG UID=1000
ARG GID=1000
RUN usermod -u ${UID} www-data && groupmod -g ${GID} www-data
USER www-data
docker compose build --build-arg UID=$(id -u) --build-arg GID=$(id -g)
3. Untuk data service (database) pakai named volume, bukan bind mount. Docker mengatur kepemilikannya dan masalah UID praktis hilang.
4. chown di Dockerfile untuk folder yang ditulis aplikasi:
RUN chown -R www-data:www-data storage bootstrap/cache
Catatan macOS: Docker Desktop memetakan kepemilikan file bind mount secara otomatis, jadi masalah UID jauh lebih jarang dibanding di Linux. Jangan kaget kalau setup yang βjalan di Macβ error di server Linux.
7. Membersihkan Volume
docker volume ls -f dangling=true # volume yang tidak dipakai container mana pun
docker volume prune # hapus volume anonim yang tidak dipakai
docker volume prune -a # hapus SEMUA volume tidak terpakai (termasuk named)
docker system df -v # lihat ukuran per volume
8. Pitfalls
docker compose down -vmenghapus named volume project β data DB hilang. Backup dulu.- Bind mount menutupi isi image. Kalau image sudah punya
vendor/lalu kamu bind mount./:/var/www/html,vendor/dari image βhilangβ. Solusinya named volume khusus untukvendor/. - Typo path dengan
-vβ Docker membuat folder kosong di host. Pakai--mountagar error. - Nama volume Compose diberi prefix nama project (
myapp_mysql-data). Saat backup dengandocker run, gunakan nama lengkapnya (cekdocker volume ls). - Volume anonim (
-v /datatanpa nama) sulit dilacak dan menumpuk. Selalu beri nama. - Upgrade versi mayor database (MySQL 8.0 β 8.4, Postgres 16 β 17) tidak selalu kompatibel dengan data volume lama. Dump β restore lebih aman.
Referensi: Volumes , Bind mounts , tmpfs mounts .

Hey! I’m Fanny, the software engineer tending to this digital garden. You can read more about me, or subscribe by email.