# Docker Volume untuk Pemula: Volume, Bind Mount, dan tmpfs

> Docker volume untuk pemula: beda volume, bind mount, dan tmpfs, kapan volume baru diisi dari image, siapa pemilik file-nya, dan apa yang dihapus down -v.

URL: https://www.matthewswong.com/id/learn/devops-from-zero/docker-volumes-vs-bind-mounts/

DevOps dari nol · Pelajaran 20 dari 25 · Ditinjau pada 18 Oktober 2026 · Oleh Matthews Wong (https://www.matthewswong.com/id/about/)

> Docker volume adalah penyimpanan yang dibuat dan dikelola Docker di luar container mana pun, sehingga datanya tetap ada saat container dihapus. Bind mount justru menampilkan folder dari host, lengkap dengan file dan pemiliknya. tmpfs mount hanya menyimpan file di memori. Tanpa salah satunya, semua tulisan masuk ke writable layer container dan ikut hilang bersamanya.

## Setelah pelajaran ini, Anda bisa

- Memilih named volume, bind mount, atau tmpfs untuk sebuah data, dan menjelaskan kapan masing-masing bertahan atau hilang.
- Menjelaskan kapan Docker menyalin file dan pemiliknya dari image ke volume baru, dan memperbaiki mount yang tidak bisa ditulis aplikasi non-root.
- Mem-backup dan me-restore named volume dengan container sekali pakai, dan tahu perintah Compose mana yang menghapusnya.

## Di mana data container disimpan

Tulisan container masuk ke writable layer dan hilang saat deploy membuat ulang container; named volume bertahan lebih lama dari container dan hanya diisi dari image selama masih kosong; bind mount menampilkan folder host dengan pemilik dari host; dan docker compose down -v menghapus volume itu.

1. **Kenali bagiannya.** Sebuah container yang berjalan sebagai uid 1001, dengan writable layer miliknya sendiri. Di luarnya: image asal container itu, named volume yang dikelola Docker, dan sebuah folder di host.
2. **Tulisan masuk ke layer.** Tanpa mount, semua yang ditulis aplikasi masuk ke writable layer container. next/image menyimpan setiap cover yang sudah dioptimasi di .next/cache/images, jadi di situlah cache-nya berada.

   ```
   /app/.next/cache/images
   ```

   *Tebak:* Deploy menarik image baru dan membuat ulang container. Apa yang terjadi pada cover yang di-cache di writable layer lama? (Container baru mewarisinya / Ikut terhapus bersama container lama)
3. **Deploy membuangnya.** docker compose up -d dengan image baru membuat ulang container. Writable layer lama ikut terhapus bersama container lama, jadi setiap cover harus dioptimasi lagi dari awal.

   ```
   docker compose up -d
   ```
4. **Volume, diisi sekali.** Pasang named volume kosong di sana, dan Docker lebih dulu menyalin isi image di path itu, beserta pemiliknya. Jika image tidak punya folder di sana, volume tetap milik root.

   ```
   - next-image-cache:/app/.next/cache/images
   ```

   *Tebak:* Deploy berikutnya membuat ulang container, dan cache-nya kini ada di named volume. Apa yang ditemukan container baru di sana? (Cover yang sama, masih ter-cache / Cache kosong lagi / Salinan baru dari image yang baru)
5. **Recreate tidak menyentuh volume.** Volume hidup di luar container mana pun. Compose membuat ulang container, memasang volume yang sama, dan cover yang sudah di-cache masih ada. Tidak ada yang disalin lagi, karena volume-nya tidak kosong.

   ```
   docker compose pull && docker compose up -d
   ```
6. **Bind mount: host yang menang.** Ganti dengan bind mount, dan container melihat folder host apa adanya, termasuk pemiliknya, tanpa ada yang disalin. Folder yang dibuat -v di Linux milik root, jadi uid 1001 tidak bisa menulis.

   ```
   - ./cache:/app/.next/cache/images
   ```
7. **down -v menghapusnya.** docker compose down menghapus container dan network, tetapi named volume tetap ada. Tambahkan -v, dan Compose ikut menghapus volume yang dideklarasikan file itu, beserta seluruh isinya.

   ```
   docker compose down -v
   ```

## Apa itu Docker volume?

Docker volume adalah penyimpanan yang dibuat dan dikelola Docker untuk container, di luar container mana pun. Ini penting karena ke mana data pergi kalau tidak ada volume: semua yang ditulis container masuk ke writable layer, yang hanya milik container itu, sulit diambil datanya oleh proses lain, dan menurut dokumentasi Docker tidak bertahan saat container dihapus. Container diganti setiap kali deploy, jadi apa pun yang perlu disimpan harus tinggal di tempat lain.

Docker menyediakan tiga tempat untuk itu: volume, bind mount, dan tmpfs mount. Data volume berada di direktori penyimpanan milik Docker di host, dan docker volume inspect menunjukkan lokasinya. Isinya hidup di luar siklus hidup container: hapus container-nya, volume tetap ada, bahkan saat tidak ada yang memakainya. Docker menyebut volume sebagai mekanisme yang disarankan untuk menyimpan data yang dibuat dan dipakai container, dan lebih mudah di-backup atau dipindahkan dibanding bind mount.

Named volume punya nama yang Anda pilih, seperti next-image-cache milik situs ini, sehingga setiap container baru bisa memasang volume yang sama. Volume anonim mendapat nama acak; jalankan container-nya dengan --rm, maka Docker menghapus volume anonim itu saat container berhenti.

## Apa bedanya dengan bind mount?

Bind mount memasukkan file atau folder dari host ke dalam container. Path-nya Anda tentukan sendiri, dan host serta container membaca dan menulis file yang sama. Itu yang Anda butuhkan untuk source code saat development, dan Docker menyarankan bind mount setiap kali kedua sisi perlu mengakses file-nya. Ini juga berisiko: secara default bind mount punya akses tulis ke file host, jadi tambahkan ro kalau container cukup membaca saja.

Bind mount membawa folder host apa adanya, termasuk pemilik dan permission-nya. Jika path di host belum ada, -v membuatnya, selalu sebagai folder, sedangkan --mount berhenti dengan error; Compose juga membuatnya kecuali create_host_path dimatikan. Dalam uji coba di Docker Engine 29.8.1 untuk Linux, folder yang dibuat -v dimiliki root dengan mode 755, dan proses yang berjalan sebagai uid 1001 mendapat Permission denied. Di Docker Desktop untuk Mac, dengan folder yang dibagikan dari macOS, penulisan yang sama berhasil, jadi coba kasus ini di mesin Linux.

## Apa yang terjadi saat mount menimpa file yang sudah ada di image?

Pasang mount apa pun di atas folder yang sudah diisi image, maka file image di sana tersembunyi selama mount itu ada. Bind mount selalu menyembunyikannya, begitu juga volume yang sudah berisi file. Dokumentasi Docker menambahkan bahwa tidak ada cara mudah untuk memunculkan kembali file yang tersembunyi; buat ulang container tanpa mount itu.

Pengecualiannya adalah volume kosong, termasuk setiap volume yang baru dibuat. Secara default Docker lebih dulu menyalin file yang sudah ada di folder itu ke dalamnya, yang oleh dokumentasinya disebut cara yang baik untuk mengisi data di awal. Referensi Dockerfile menyatakan hal yang sama untuk VOLUME: docker run mengisi volume baru dengan apa pun yang ada di lokasi itu di dalam image. Opsi volume-nocopy, atau nocopy di Compose, mematikan penyalinan ini, dan bind mount tidak pernah diisi dengan cara ini.

Pemilik file ikut tersalin. Dokumentasi Docker tidak menuliskannya secara eksplisit, jadi hal ini diuji di Docker Engine 29.8.1: volume baru yang dipasang di folder milik uid 1001 di dalam image langsung dimiliki uid 1001. Dipasang di path yang tidak ada di image, volume sejenis justru dimiliki root, dan uid 1001 tidak bisa menulis ke sana. Penyalinan juga hanya terjadi selama volume masih kosong: volume yang sudah berisi tetap menyimpan file dan pemiliknya, walaupun image baru datang.

## Kapan tmpfs jadi pilihan yang tepat?

tmpfs mount bersifat sementara dan hanya hidup di memori host. Saat container berhenti, mount itu dihapus beserta file-nya, dan container tidak bisa berbagi satu tmpfs. Docker mendokumentasikannya untuk Docker di Linux; Docker Desktop menjalankan container di VM Linux, dan di sanalah langkah coba sendiri di bawah mengujinya.

Pakai untuk file sementara yang tidak perlu ada di disk. Dua batasannya menurut dokumentasi Docker: yang Anda tulis di sana dihitung ke batas memori container, dan kernel tetap bisa memindahkannya ke swap di disk. Dengan --mount tanpa ukuran, tmpfs bisa tumbuh sampai separuh RAM host.

## Kenapa situs ini menyimpan cache gambarnya di named volume?

next/image mengoptimasi setiap cover saat pertama kali diminta, lalu menyimpan hasilnya di folder cache/images di direktori build Next.js, di sini .next/cache/images. Tanpa mount, folder itu berada di writable layer, dan setiap deploy menjalankan docker compose up -d dengan image baru, yang menghentikan lalu membuat ulang container. Referensi Docker menyebut recreate itu mempertahankan volume yang terpasang, jadi named volume membawa cache dari satu container ke container berikutnya.

Komentar di file compose juga menjelaskan kenapa bukan bind mount: folder host yang dibuat Docker akan dimiliki root, sedangkan named volume akan mewarisi pemilik uid 1001 dari image. Kedua aturan itu benar, tetapi yang kedua butuh folder di path itu di dalam image, dan stage runner tidak membuatnya: stage itu hanya menyalin output standalone Next, yang tidak punya folder cache, dan file static. Dengan kondisi file saat ini, volume itu kemungkinan besar dimulai dalam keadaan milik root, sama seperti temuan pelajaran 5. Perbaikannya satu baris RUN di runner, sebelum USER nextjs, yang membuat .next/cache/images dan menyerahkannya ke nextjs.

## Apa yang menghapus volume?

Menghapus container tidak pernah menghapus named volume; Docker menyimpannya sampai Anda sendiri yang menghapus. docker compose down menghapus container dan network project, dan membiarkan named volume, bahkan volume anonim, tetap ada. docker compose down -v juga menghapus named volume yang dideklarasikan di bagian volumes file itu serta volume anonim yang terpasang di container-nya. Volume bertanda external tidak pernah dihapus. Deploy situs ini menjalankan docker compose pull lalu up -d, tidak pernah down -v, jadi volume cache-nya bertahan melewati setiap rilis.

docker volume rm dan docker volume prune menghapus volume secara langsung; pelajaran 15 membahas flag prune mana yang ikut menyentuh named volume.

## Bagaimana cara mem-backup named volume?

Dengan container sekali pakai. Resep Docker menjalankan container dengan --rm yang memasang volume dan sebuah folder host, menjalankan tar untuk menulis arsip ke folder host itu, lalu selesai; restore adalah kebalikannya, ke volume baru. Contoh di dokumentasi Docker menjangkau volume lewat --volumes-from; memasangnya lewat nama, seperti di langkah coba sendiri di bawah, bekerja sama saja.

Arsip tar hanya sekonsisten file-nya saat dibaca tar. Dokumentasi PostgreSQL menyatakan backup level file system mengharuskan server dimatikan, karena tar tidak mengambil snapshot atomik; untuk database, hentikan dulu atau pakai tool dump miliknya. Volume situs ini hanya berisi cache yang bisa dibuat ulang, jadi tidak perlu di-backup sama sekali.

## Konfigurasi nyata di balik pelajaran ini

`docker-compose.yml:14-23`

```yaml
    volumes:
      # Persist next/image's optimised-variant cache across deploys.
      # next.config.ts sets minimumCacheTTL to a year, but the cache lived in the
      # container's writable layer and every deploy replaces the container — so
      # the first request for each cover after a deploy paid a full JPEG decode
      # plus an AVIF encode again, on 1 vCPU under a 512 MB cap. A named volume
      # rather than a bind mount, so Docker seeds its ownership from the image's
      # uid 1001 (Dockerfile:35) instead of creating a root-owned host dir the
      # container cannot write to.
      - next-image-cache:/app/.next/cache/images
```

Komentar ini memuat kedua alasan desainnya. Deploy mengganti container, dan Compose mempertahankan volume yang terpasang saat membuat ulang container, jadi cache-nya bertahan. Bagian soal pemilik hanya berlaku jika image punya folder di /app/.next/cache/images untuk disalin Docker, dan tidak ada yang membuatnya di stage runner. Rujukan barisnya juga sudah bergeser: user uid 1001 sekarang dibuat di baris 63 Dockerfile.

`docker-compose.yml:49-50`

```yaml
volumes:
  next-image-cache:
```

Entri di level atas ini mendeklarasikan volume tanpa opsi, jadi Docker membuatnya dengan pengaturan default engine saat docker compose up pertama kali berjalan, lalu memakainya ulang setelah itu. Compose menambahkan nama project sebagai awalan, karena itu docker volume ls menampilkan nama yang lebih panjang daripada di file. Volume ini hanya terhapus jika sengaja dihapus, atau lewat docker compose down -v.

`Dockerfile:66-67`

```dockerfile
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
```

Dua baris ini menyerahkan server standalone, folder .next-nya, dan file static kepada uid 1001. Output standalone Next tidak punya folder cache, seperti yang terlihat di build lokal, jadi image tidak punya apa pun di .next/cache/images untuk disalin Docker ke volume baru. Membuat folder itu di runner, dengan pemilik nextjs, adalah langkah yang diandaikan oleh komentar compose.

## Yang sering rusak

- **Permission denied di volume atau bind mount baru.** Aplikasi berjalan sebagai user non-root, sedangkan mount point-nya milik root: folder host yang dibuat -v, atau volume baru di path yang tidak pernah ada di image. Buat folder itu di Dockerfile dan serahkan ke user aplikasi sebelum USER, atau chown folder host ke uid aplikasi. Periksa dengan stat di dalam container: pemiliknya berupa angka, dan 0 berarti root.
- **Database kosong setelah docker compose down -v.** -v terlihat seperti flag untuk bersih-bersih, padahal flag itu menghapus named volume yang dideklarasikan file, beserta datanya, bukan hanya container. Pakai down biasa untuk menghentikan project. Kalau memang ingin menghapus volume, backup dulu dengan container sekali pakai.
- **Bind mount menyembunyikan isi image.** Pasang folder host di atas path yang sudah diisi image, misalnya kode aplikasi itu sendiri atau folder html milik nginx, maka file-file itu hilang di dalam container karena tertutup mount. Pasang di path terpisah, atau pakai named volume, yang saat masih kosong akan diisi dari image.
- **Image baru, volume lama.** Docker hanya menyalin file image ke volume yang masih kosong. Kirim file default baru lewat image baru, maka volume yang sudah ada tetap menyimpan file lama, karena isinya menutupi isi image. Jauhkan file default dari path yang di-mount, atau perbarui isi volume sendiri.

### Named volume, bind mount, atau tmpfs: di mana data disimpan dan apa yang bisa dilewatinya, sesuai dokumentasi Docker.

| Mount | Lokasi data | Setelah container dihapus | Saat pertama dipasang | Cocok untuk |
| --- | --- | --- | --- | --- |
| Named volume | Area penyimpanan Docker di host, dikelola Docker | Tetap ada sampai Anda menghapusnya atau menjalankan down -v | Jika kosong, berisi salinan folder image di path itu | Data aplikasi, database, cache yang harus bertahan antar deploy |
| Bind mount | Path host yang Anda pilih | Tetap ada: itu folder milik host | Folder host apa adanya, menutupi file image | Source code saat development, file konfigurasi yang diedit di host |
| tmpfs | Memori host | Hilang: dihapus saat container berhenti | Folder kosong | File sementara yang tidak perlu ada di disk |

## Coba sendiri

Anda butuh Docker di laptop dan terminal (macOS, Linux, atau WSL). Semua langkah berjalan di satu folder kosong, semua nama diawali demo-, dan langkah terakhir menghapus semuanya.

1. Buat folder kerja, buat named volume, lalu inspect. Mountpoint adalah path di area penyimpanan milik Docker; di Docker Desktop path itu ada di dalam VM Linux-nya, bukan di disk laptop Anda.

   ```bash
   mkdir demo-volumes && cd demo-volumes
   docker volume create demo-data
   docker volume inspect demo-data
   ```
2. Tulis file dari satu container sekali pakai, lalu baca dari container lain. Container pertama sudah tidak ada; file-nya masih ada dan mencetak hello.

   ```bash
   docker run --rm -v demo-data:/data alpine:3 sh -c 'echo hello > /data/note.txt'
   docker run --rm -v demo-data:/data alpine:3 cat /data/note.txt
   ```
3. Pasang volume baru di atas folder html milik nginx, lalu folder host kosong di path yang sama. Volume menampilkan 50x.html dan index.html, hasil salinan dari image; bind mount tidak menampilkan apa-apa, karena menutupinya.

   ```bash
   docker run --rm -v demo-html:/usr/share/nginx/html nginx:alpine ls /usr/share/nginx/html
   mkdir site
   docker run --rm -v "$PWD/site":/usr/share/nginx/html nginx:alpine ls /usr/share/nginx/html
   ```
4. Pemilik file ikut tersalin. node:22-alpine punya user node, uid 1000, pemilik /home/node, jadi volume baru di sana mencetak 1000. Di path yang tidak ada di image, volume mencetak 0, yaitu root, dan user node mendapat Permission denied.

   ```bash
   docker run --rm -v demo-home:/home/node node:22-alpine stat -c '%u %n' /home/node
   docker run --rm -u node -v demo-missing:/home/node/cache node:22-alpine \
     sh -c 'stat -c "%u %n" /home/node/cache; touch /home/node/cache/x'
   ```
5. tmpfs mount hidup di memori: df menampilkan tmpfs sebagai filesystem-nya, dan tidak ada yang ditulis di sana bertahan setelah container selesai.

   ```bash
   docker run --rm --tmpfs /scratch alpine:3 df -h /scratch
   ```
6. Backup volume dengan container sekali pakai yang menulis arsip tar ke folder ini, lalu restore ke volume baru dan baca lagi file-nya.

   ```bash
   docker run --rm -v demo-data:/data:ro -v "$PWD":/backup alpine:3 tar czf /backup/demo-data.tgz -C /data .
   docker volume create demo-restored
   docker run --rm -v demo-restored:/data -v "$PWD":/backup alpine:3 tar xzf /backup/demo-data.tgz -C /data
   docker run --rm -v demo-restored:/data alpine:3 cat /data/note.txt
   ```
7. Sekarang dengan Compose: satu service dan satu named volume. Tulis file, paksa container yang benar-benar baru, lalu baca file-nya lagi. docker compose ps -q mencetak ID container yang berbeda di kali kedua; file-nya tetap berisi kept.

   ```bash
   printf 'services:\n  app:\n    image: nginx:alpine\n    volumes:\n      - data:/data\nvolumes:\n  data:\n' > compose.yaml
   docker compose up -d
   docker compose exec app sh -c 'echo kept > /data/note.txt'
   docker compose ps -q
   docker compose up -d --force-recreate
   docker compose ps -q
   docker compose exec app cat /data/note.txt
   ```
8. docker compose down membiarkan volume-nya, yang tercantum sebagai demo-volumes_data, jadi file-nya selamat saat up lagi. down -v menghapusnya, dan daftar terakhir kembali kosong.

   ```bash
   docker compose down
   docker volume ls --filter name=demo-volumes
   docker compose up -d
   docker compose exec app cat /data/note.txt
   docker compose down -v
   docker volume ls --filter name=demo-volumes
   ```
9. Bersihkan volume dan folder-nya.

   ```bash
   docker volume rm demo-data demo-html demo-home demo-missing demo-restored
   cd .. && rm -rf demo-volumes
   ```

## Kuis singkat

1. **Sebuah service Postgres menyimpan datanya di named volume. Anda menjalankan docker compose down, lalu docker compose up -d. Apa yang terjadi pada datanya?**
   A. Terhapus, karena down menghapus semua isi project
   B. Tetap ada, karena down tidak menyentuh named volume
   C. Tetap ada hanya jika image-nya tidak berubah
   Jawaban: B. down menghapus container dan network. Named volume yang dideklarasikan di file tetap selamat, apa pun image-nya, kecuali Anda menambahkan -v.

2. **Sebuah named volume baru dipasang di /app/uploads, path yang tidak ada di image. Aplikasi berjalan sebagai uid 1001. Apa yang terjadi saat aplikasi menulis ke sana?**
   A. Permission denied: volume itu sejak awal milik root
   B. Berhasil: Docker menyerahkan volume baru ke user container
   C. Berhasil: volume bisa ditulis siapa saja
   Jawaban: A. Docker menyalin file dan pemiliknya ke volume kosong hanya dari folder yang ada di path itu di dalam image. Kalau tidak ada, volume tetap milik root. Buat folder itu di image, dengan pemilik uid 1001, sebelum USER.

3. **Anda melakukan bind mount folder host yang kosong ke /usr/share/nginx/html di image nginx. Apa isi folder itu di dalam container?**
   A. index.html milik image, disalin ke folder host
   B. File image digabung dengan isi folder host
   C. Kosong: folder host yang kosong menutupi file image
   Jawaban: C. Bind mount tidak pernah menyalin. Yang terlihat adalah folder host, dan apa pun yang ada di path itu di dalam image tersembunyi sampai mount dilepas. Hanya volume kosong yang mendapat salinan.

## Pikirkan dulu

- **Kenapa cache gambar situs ini butuh volume?** Setiap deploy membuat ulang container, dan writable layer ikut terhapus bersama container lama. Named volume hidup di luar container, jadi Compose memasang volume yang sama ke setiap container baru.
- **Kapan Docker menyalin file image ke volume, dan kapan tidak?** Hanya saat volume masih kosong ketika dipasang dan image punya isi di path itu. Volume yang sudah berisi tetap menyimpan isinya, ada image baru atau tidak, dan bind mount tidak pernah diisi.
- **Perintah mana yang menghapus named volume milik project Compose?** docker compose down -v, docker volume rm, dan volume prune dengan flag yang ikut menyasar named volume. down biasa, recreate, dan menghapus container sama sekali tidak menyentuh named volume.

## Pelajari lebih dalam

- [Belajar Docker dari Nol: Container Pertama dalam 6 Langkah](https://www.matthewswong.com/id/blog/belajar-docker-dari-nol-panduan-pemula/)
- [Strategi Backup PostgreSQL untuk VPS: Dari pg_dump hingga WAL Archiving](https://www.matthewswong.com/id/blog/postgresql-backup-strategy/)
- [restic vs BorgBackup: Backup Terenkripsi dan Terdeduplikasi](https://www.matthewswong.com/id/blog/restic-vs-borg-encrypted-backups/)

## Sumber

- [Dokumentasi Docker: ikhtisar storage (writable layer, jenis mount)](https://docs.docker.com/engine/storage/)
- [Dokumentasi Docker: volume (siklus hidup, pengisian awal, backup dan restore)](https://docs.docker.com/engine/storage/volumes/)
- [Dokumentasi Docker: bind mount](https://docs.docker.com/engine/storage/bind-mounts/)
- [Dokumentasi Docker: tmpfs mount](https://docs.docker.com/engine/storage/tmpfs/)
- [Dokumentasi Docker: referensi Dockerfile, VOLUME](https://docs.docker.com/reference/dockerfile/#volume)
- [Dokumentasi Docker: volumes di level atas Compose](https://docs.docker.com/reference/compose-file/volumes/)
- [Dokumentasi Docker: referensi service Compose, volumes](https://docs.docker.com/reference/compose-file/services/#volumes)
- [Dokumentasi Docker: docker compose up (recreate mempertahankan volume)](https://docs.docker.com/reference/cli/docker/compose/up/)
- [Dokumentasi Docker: docker compose down (-v, --volumes)](https://docs.docker.com/reference/cli/docker/compose/down/)
- [Dokumentasi Next.js: komponen Image (lokasi cache gambar teroptimasi)](https://nextjs.org/docs/app/api-reference/components/image)
- [Dokumentasi PostgreSQL: backup level file system](https://www.postgresql.org/docs/current/backup-file.html)

---

- HTML version: https://www.matthewswong.com/id/learn/devops-from-zero/docker-volumes-vs-bind-mounts/
- English: https://www.matthewswong.com/en/learn/devops-from-zero/docker-volumes-vs-bind-mounts/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
