SSD Nodes Learn Hosting plans →
Panduan Matt ConnorOleh Matt Connor · Diperbarui 2026-10-30

Docker Compose: Pilih Bind Mount atau Named Volume

Pahami kapan memakai bind mount atau named volume untuk konfigurasi dan data, jebakan izin file, serta cara memeriksa, mencadangkan, dan memigrasikan volume.

Bind mount atau named volume: jawaban singkat

Volume Docker Compose memiliki dua jenis, dan pilihannya bergantung pada pihak yang memiliki file tersebut. Gunakan bind mount untuk file yang Anda tulis dan baca sendiri, seperti konfigurasi, template, dan situs statis. Gunakan named volume untuk data yang dikelola oleh aplikasi, seperti file database, indeks pencarian, dan media yang diunggah. Bind mount menunjuk ke path pada host yang dapat Anda buka di editor. Named volume adalah penyimpanan yang dibuat dan dilacak oleh Docker, dan Anda mengaksesnya melalui Docker.

Keduanya ditulis di bawah key volumes: yang sama di dalam sebuah service. Karena itu, keduanya sering tertukar. Perbedaannya terletak pada sisi kiri titik dua. Sisi kiri yang diawali . atau / adalah path host, sehingga merupakan bind mount. Selain itu, sisi kiri tersebut dianggap sebagai nama, sehingga merupakan named volume. Nama itu juga harus dideklarasikan di dalam blok volumes: tingkat atas.

Dua sintaks dalam file compose

services:
  db:
    image: postgres:17
    environment:
      POSTGRES_PASSWORD: changeme
    volumes:
      - pgdata:/var/lib/postgresql/data
  web:
    image: nginx:1.27
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./site:/usr/share/nginx/html:ro

volumes:
  pgdata:

pgdata:/var/lib/postgresql/data adalah volume bernama. ./nginx.conf:/etc/nginx/nginx.conf adalah bind mount, dan :ro memasangnya dalam mode hanya-baca. Ini merupakan default yang tepat untuk konfigurasi yang tidak boleh ditulis ulang oleh container. Jika entri tingkat teratas volumes: diabaikan, Compose berhenti dengan service "db" refers to undefined volume pgdata.

Jalankan compose dan tampilkan resource yang dibuat Docker:

docker compose up -d
docker volume ls

Nama volume tersebut bukan pgdata. Namanya adalah <project>_pgdata, karena nama project secara default mengikuti nama direktori yang berisi file compose. Direktori bernama myapp menghasilkan myapp_pgdata. Hal ini penting karena mengganti nama direktori akan menghasilkan volume kosong baru, sehingga aplikasi tampak kehilangan datanya. Data tersebut tidak hilang: volume lama masih ditampilkan oleh docker volume ls. Tetapkan namanya dengan name: di file compose, atau atur COMPOSE_PROJECT_NAME jika direktori mungkin dipindahkan. Pengaturan seperti ini sebaiknya ditempatkan bersama file environment dan secret Compose lainnya.

Mengapa error permission paling sering terjadi pada bind mount

Ini adalah perbedaan praktis terbesar. Penyebab utamanya adalah satu aturan: named volume yang masih kosong saat pertama kali digunakan akan diisi dari image, sedangkan bind mount tidak.

Saat Docker memasang named volume kosong pada direktori yang sudah berisi konten di dalam image, Docker menyalin konten tersebut ke volume, termasuk ownership dan mode yang ditetapkan image. Official Postgres image menyediakan /var/lib/postgresql/data yang dimiliki oleh user postgres miliknya sendiri. Karena itu, volume menjadi dimiliki oleh id numerik yang sama dan database dapat berjalan.

Pengisian awal tersebut adalah satu-satunya keunggulan awal yang dimiliki named volume. Docker mengelola lokasi penyimpanan volume, tetapi container membaca dan menulisnya menggunakan operasi filesystem biasa. Karena itu, ownership dan mode tetap diberlakukan seperti pada bind mount. Named volume yang dipasang pada path yang tidak ada di image, atau yang pertama kali diisi oleh image atau user lain, tidak memperoleh ownership dari image. Proses non-root juga dapat ditolak saat mengaksesnya. Pemeriksaan id di bawah ini berlaku untuk kedua jenis mount.

Bind mount bekerja sebaliknya. Apa pun yang ada di host akan terlihat oleh container, termasuk ownership, dan konten image pada path tersebut menjadi tersembunyi. Jika direktori pada host tidak ada, Docker daemon membuatnya. Daemon berjalan sebagai root, sehingga direktori tersebut dimiliki oleh root:root. Proses container yang berjalan sebagai user non-root kemudian tidak dapat menulis ke direktori itu:

PermissionError: [Errno 13] Permission denied: '/data/app.db'

Solusinya adalah menyamakan nilai numeriknya. Ownership pada bind mount dibandingkan berdasarkan numeric user id, bukan nama, karena container memiliki /etc/passwd sendiri. User bernama app di dalam container tidak memiliki arti di host. Uid 1000 berarti uid 1000 di kedua sisi, kecuali daemon menggunakan user-namespace remapping (userns-remap, yang diatur dalam /etc/docker/daemon.json atau pada command line dockerd). Remapping menggeser setiap id container ke subordinate range di host. Dalam contoh range Docker yang dimulai dari 231072, container uid 0 menjadi host uid 231072. Jadi, container uid 1000 menjadi host uid 232072.

id -u
mkdir -p ./data
docker compose exec web id
sudo chown -R 1000:1000 ./data

docker compose exec web id menampilkan uid yang benar-benar digunakan oleh proses container. Tanpa remapping, samakan ownership direktori host dengan angka tersebut, atau tetapkan container agar menggunakan uid Anda dengan user: "1000:1000" di dalam service. Jika remapping aktif, user: tidak dapat menetapkan container ke nomor host Anda, karena id yang Anda tetapkan juga digeser: user: "1000:1000" tetap menjadi host uid 232072 dalam contoh range tersebut. Sebagai gantinya, ubah ownership direktori host ke host id hasil pemetaan. Range milik Anda tercantum dalam /etc/subuid dan /etc/subgid. Dokumentasi Docker hanya menyebut --userns=host pada docker container create, run atau exec sebagai cara menonaktifkan remapping untuk satu container. Menetapkan user: lebih bersih untuk aplikasi yang Anda tulis sendiri. Mengubah ownership direktori host lebih aman untuk image yang tidak Anda tulis, karena beberapa image memulai entrypoint sebagai root, menurunkan privilege, lalu mengharapkan ownership tertentu pada direktori di dalamnya.

Ada dua jebakan lain yang perlu diketahui. Pada Fedora, RHEL, dan sistem lain dengan SELinux (security-enhanced Linux) dalam mode enforcing, bind mount ditolak sampai diberi relabel. Karena itu, tambahkan :z untuk path yang digunakan bersama oleh beberapa container atau :Z untuk path yang hanya boleh digunakan oleh satu container, dengan penulisan - ./data:/data:Z. Selain itu, bind mount untuk satu file, bukan direktori, dapat bermasalah ketika editor mengganti file tersebut alih-alih menulis langsung ke file yang sama, karena mount mengikuti inode asli. Container akan terus melihat konten lama sampai Anda me-restart-nya. Jika file sering diedit, mount direktori induknya.

Performa: perbedaan yang nyata

Pada server Linux, kedua jenis tersebut melewati jalur kernel yang sama. Karena itu, perbedaan throughput cukup kecil sehingga Anda tidak perlu memilih berdasarkan faktor tersebut. Named volume yang menggunakan driver local default berada pada filesystem yang sama dengan bagian Docker lainnya, di bawah /var/lib/docker/volumes/. Bind mount berada di lokasi yang Anda tentukan.

Perbedaan terlihat pada Docker Desktop untuk macOS dan Windows, karena container berjalan di dalam virtual machine. Bind mount di sana melintasi filesystem host menuju virtual machine melalui lapisan berbagi file. Beban kerja dengan banyak operasi file kecil, seperti dependency tree Node.js atau cache framework PHP, menjadi lebih lambat secara nyata. Named volume tetap berada di dalam virtual machine dan tidak menanggung biaya tersebut. Karena itu, banyak file Compose untuk pengembangan menggunakan bind mount pada direktori source, tetapi mendeklarasikan named volume pada node_modules.

Perbedaan nyata lainnya adalah lokasi penyimpanan byte. Bind mount ke /mnt/backup menyimpan data pada disk tersebut. Named volume disimpan pada filesystem yang memuat /var/lib/docker, yang pada VPS biasanya merupakan root disk. Database yang terus membesar di dalam named volume akan memenuhi disk yang sama dengan disk tempat log sistem disimpan. Periksa sebelum masalah terjadi:

docker system df -v
df -h /var/lib/docker

docker system df -v mencantumkan setiap volume beserta ukurannya dan menandai volume yang tidak lagi digunakan oleh container mana pun.

Memeriksa named volume

Named volume bukan kotak hitam. Tanyakan lokasinya kepada Docker:

docker volume inspect myapp_pgdata

Kolom Mountpoint berisi path host yang sebenarnya, biasanya /var/lib/docker/volumes/myapp_pgdata/_data. Anda dapat membacanya dengan sudo ls, dan cara ini berguna untuk pemeriksaan cepat. Jangan menganggapnya sebagai lokasi untuk mengedit file. Menulis di sana sebagai root akan mengulangi masalah kepemilikan yang dijelaskan di atas, dan path tersebut merupakan detail driver local yang tidak dimiliki driver volume lainnya.

Cara aman untuk melihat isinya adalah menggunakan container sementara yang memasang volume tersebut:

docker run --rm -v myapp_pgdata:/vol alpine ls -la /vol

Cara ini berfungsi untuk driver apa pun, melihat permission yang sama seperti yang dilihat container sebenarnya, dan tidak meninggalkan apa pun karena --rm.

Mencadangkan setiap jenis

Bind mount adalah direktori biasa, sehingga tool pencadangan berbasis file sudah dapat menanganinya. Arahkan pencadangan ke path host, lalu selesai. Named volume memerlukan satu langkah tambahan karena tool tersebut harus mengakses isinya. Mount volume dan direktori host ke dalam container sementara yang sama, lalu buat arsip:

docker run --rm \
  -v myapp_pgdata:/data:ro \
  -v "$PWD":/backup \
  alpine tar czf /backup/pgdata.tar.gz -C /data .

Pulihkan dengan membalik proses tersebut ke volume baru:

docker volume create myapp_pgdata_restored
docker run --rm \
  -v myapp_pgdata_restored:/data \
  -v "$PWD":/backup \
  alpine tar xzf /backup/pgdata.tar.gz -C /data

tar mempertahankan kepemilikan numerik saat dijalankan sebagai root di dalam container. Hal ini membuat volume yang dipulihkan tetap dapat digunakan oleh aplikasi.

Satu peringatan berlaku untuk kedua jenis tersebut. Menyalin file database saat database masih berjalan akan menghasilkan arsip dari data yang terus berubah. Arsip tersebut dapat dipulihkan dalam kondisi rusak. Hentikan service terlebih dahulu, atau buat dump menggunakan tool bawaan database, seperti pada docker compose exec -T db pg_dump -U postgres appdb > appdb.sql. Proses ini menghasilkan file biasa yang kemudian dapat Anda sertakan dalam rutinitas pencadangan restic terenkripsi bersama file compose.

Migrasi bind mount ke named volume

Pemindahan ini merupakan penyalinan, bukan penggantian nama, dan memerlukan waktu sekitar satu menit.

docker compose down
docker volume create myapp_pgdata
docker run --rm \
  -v "$PWD/data":/from \
  -v myapp_pgdata:/to \
  alpine sh -c 'cp -a /from/. /to/'

cp -a mempertahankan ownership, mode, dan timestamp, sehingga user container yang dapat membaca direktori lama tetap dapat membaca volume baru. Kemudian ubah service agar menggunakan pgdata:/var/lib/postgresql/data, tambahkan pgdata ke bagian tingkat teratas volumes:, jalankan docker compose up -d, lalu baca log aplikasi sebelum menghapus direktori lama. Untuk melakukan sebaliknya, gunakan perintah yang sama dengan menukar /from dan /to.

Ingat satu hal saat melakukan pengujian. docker compose down tidak mengubah named volume, tetapi docker compose down -v menghapus semua named volume yang dideklarasikan project, dan tindakan ini tidak dapat dibatalkan. Bind mount tetap ada setelah kedua perintah tersebut karena Docker tidak pernah memiliki direktori itu. Jika perintah untuk mengelola lifecycle masih baru bagi Anda, panduan dasar Docker Compose untuk VPS menjelaskannya secara bertahap.

Memilih berdasarkan service

Tentukan siapa yang menulis file tersebut. Konfigurasi yang Anda edit dengan text editor dan commit ke git sebaiknya disimpan dalam bind mount yang di-mount :ro, karena Anda ingin file tersebut terlihat dan memiliki versioning. Application state yang tidak pernah Anda buka secara manual sebaiknya disimpan dalam named volume, karena Docker mengatur permission berdasarkan image dan data tersebut tidak bergantung pada path di host.

Kasus campuran adalah media. Photo library ditulis oleh aplikasi, tetapi juga dikelola oleh Anda, dan sering kali cukup besar sehingga memerlukan disk khusus. Gunakan bind mount ke path pada disk tersebut, lalu atur ownership-nya secara sengaja satu kali. Itulah pola yang umumnya digunakan self-hosted stack: named volume untuk database dan cache, bind mount untuk konfigurasi serta direktori besar yang penting bagi Anda. Support desk seperti Chatwoot yang berjalan pada VPS tepat menggunakan pola ini, dengan Postgres dalam named volume dan attachment yang diunggah pada path yang dapat Anda tentukan sebagai sumber backup.

FAQ

Apa perbedaan antara bind mount dan named volume?

Bind mount memetakan path pada host ke dalam container. Kedua sisi melihat direktori yang sama, dan Anda dapat mengeditnya dengan tool biasa. Named volume adalah storage yang dibuat dan dikelola Docker, dirujuk berdasarkan nama, dan dideklarasikan dalam blok volumes: tingkat teratas. Perbedaan praktisnya terletak pada ownership: gunakan bind mount untuk konfigurasi yang Anda kelola, dan named volume untuk data yang dikelola aplikasi.

Mengapa saya mendapatkan "permission denied" pada bind mount, tetapi tidak pada named volume?

Named volume yang masih kosong akan diisi dari image. Karena itu, volume biasanya mewarisi ownership yang ditetapkan image dan dapat ditulis oleh user container. Permission tetap diberlakukan, sehingga masalah yang sama dapat terjadi jika image tidak memiliki apa pun pada path tersebut. Bind mount menampilkan direktori host persis seperti kondisinya. Jika Docker harus membuat direktori itu, direktori tersebut dibuat dengan ownership root. Jalankan docker compose exec <service> id untuk melihat id numerik yang digunakan container, lalu sudo chown -R <uid>:<gid> direktori host tersebut. Jika daemon tidak menggunakan user-namespace remapping, Anda juga dapat menetapkan user: "1000:1000" pada service. Jika daemon menggunakan user-namespace remapping, ubah ownership ke host id hasil pemetaan, karena id container digeser di host dan user: tidak mengubah hal itu.

Di mana Docker menyimpan named volume pada disk?

Dengan driver local bawaan, volume disimpan di bawah /var/lib/docker/volumes/<volume>/_data. docker volume inspect <volume> menampilkan Mountpoint yang tepat. Baca lokasi tersebut jika perlu melakukan pemeriksaan, tetapi hanya tulis ke volume melalui container. Mengeditnya sebagai root pada host dapat mengubah ownership dengan cara yang tidak diharapkan oleh container.

Bagaimana cara mencadangkan named volume?

Jalankan container sementara dengan volume dan direktori host yang dipasang secara bersamaan. Kemudian, arsipkan data dari satu lokasi ke lokasi lainnya menggunakan docker run --rm -v myvol:/data:ro -v "$PWD":/backup alpine tar czf /backup/myvol.tar.gz -C /data .. Untuk database, lakukan dump menggunakan tool bawaan database, bukan menyalin file yang sedang digunakan. Penyalinan file saat proses write masih berlangsung dapat menyebabkan hasil restore berada dalam kondisi rusak.

Apakah docker compose down menghapus volume saya?

docker compose down menghapus container dan network, tetapi membiarkan named volume tetap ada. docker compose down -v juga menghapus setiap named volume yang dideklarasikan oleh project secara permanen. Bind mount tidak pernah dihapus oleh kedua command tersebut, karena direktori itu milik host, bukan Docker.

Apakah artikel ini bermanfaat?