Volume adalah tempat menyimpan data di luar container, supaya datanya tetap ada walau container-nya dihapus lalu dibuat ulang. Bayangin kamu kerja di coworking space dengan meja bebas. Barang di atas meja aman selama mejanya masih kamu pakai, termasuk waktu kamu keluar makan siang. Begitu kamu check-out, mejanya dibersihkan untuk orang berikutnya. Mau barangmu awet? Taruh di loker bernama yang disewakan pengelola (named volume), atau bawa flashdisk-mu sendiri yang isinya kamu pegang (bind mount).
🧪 Tebakan cepat: database catatan-api sudah berisi 3 catatan, disimpan di named volume
db-data. Kamu menjalankandocker compose down, laludocker compose up -d. Catatannya masih 3 atau jadi 0? Dan kalau pakaidown -v? Tebak dulu, jawabannya ada di bagian "down dan down -v".
Data di dalam container ikut terhapus bersama container-nya
Setiap container punya lapisan tulis (writable layer) sendiri di atas image. File apa pun yang ditulis app di dalam container masuk ke lapisan itu. Lapisannya hidup selama container-nya hidup:
docker compose stoplalustart, ataudocker compose restart: container-nya tetap yang sama, jadi file-nya masih ada. Ini "keluar makan siang".docker compose down(ataudocker rm): container dihapus, lapisannya ikut dibuang.upberikutnya bikin container baru dari image, bersih. Ini "check-out".
Contohnya lampiran yang di-upload ke catatan-api. App menyimpannya di storage/lampiran di dalam container, tanpa volume:
$ docker compose exec app ls storage/lampiran
7f3a9c1e.jpg
$ docker compose restart app
$ docker compose exec app ls storage/lampiran
7f3a9c1e.jpg
$ docker compose down
$ docker compose up -d
$ docker compose exec app ls storage/lampiran/7f3a9c1e.jpg
ls: storage/lampiran/7f3a9c1e.jpg: No such file or directory
Format error itu milik ls di Alpine Linux, karena image app kita dibangun dari node:20-alpine. File sementara boleh hilang begini. Lampiran pengguna dan database jelas gak boleh.
Anonymous volume: volume tanpa nama
Postgres menyimpan datanya di folder /var/lib/postgresql/data di dalam container. Image postgres:16 menandai folder itu sebagai VOLUME di Dockerfile-nya, jadi kalau kamu gak menulis volumes: sama sekali, Docker diam-diam bikin anonymous volume: volume tanpa nama, cuma ID acak 64 karakter, khusus untuk container itu.
Hasilnya mirip lapisan tulis: setelah down lalu up, container baru dapat anonymous volume baru yang kosong, dan database-nya mulai dari nol. Volume lama gak terhapus, tapi tertinggal tanpa pemilik (dangling), makan disk tanpa ada yang memakainya.
Named volume: loker yang dikelola Docker
Ini docker-compose.yml catatan-api dengan named volume, sama dengan yang ada di Lab:
services:
app:
build: .
env_file: .env
ports:
- "3000:3000"
depends_on:
- db
db:
image: postgres:16
environment:
POSTGRES_USER: catatan
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:
db-data:/var/lib/postgresql/dataartinya "pasang volume bernamadb-datadi folder data Postgres". Kiri titik dua sumbernya, kanan path di dalam container.POSTGRES_USER: catatanmembuat user dan database bernamacatatan.${DB_PASSWORD}diambil Compose dari file.env, seperti di pelajaran Environment Variables & Secrets.volumes:paling bawah mendeklarasikan named volume-nya. Tanpa deklarasi ini Compose menolak jalan.- Compose memberi awalan nama project (nama folder), jadi nama aslinya
catatan-api_db-data. Di Linux, isinya ada di/var/lib/docker/volumes/catatan-api_db-data/_data. Kamu gak perlu menyentuh folder itu, cukup sebut namanya.
$ docker volume ls
DRIVER VOLUME NAME
local catatan-api_db-data
Tanpa Compose, caranya docker volume create db-data, lalu docker run -d -e POSTGRES_PASSWORD=$DB_PASSWORD -v db-data:/var/lib/postgresql/data postgres:16. Lihat isi detailnya dengan docker volume inspect db-data.
Bind mount: folder host dipasang ke container
Bind mount memasang folder yang sudah ada di komputermu (host) langsung ke dalam container. Bedanya dengan named volume: named volume dikelola Docker di lokasinya sendiri, bind mount menunjuk path host yang kamu pilih.
docker run -d -p 3000:3000 -v $(pwd):/app catatan-api
$(pwd)diganti shell dengan folder tempat kamu berada sekarang (print working directory), misalnya/home/budi/catatan-api. Docker modern juga menerima.atau./.- File yang kamu edit di laptop langsung kelihatan di
/appdalam container. Cocok untuk development, misalnya hot reload. - Di Compose ditulis
- ./src:/app/src. Path relatif diawali./.
Bind mount .:/app menutupi seluruh isi /app dari image, termasuk node_modules yang dipasang saat build. Trik umumnya: tambah baris - /app/node_modules. Baris tanpa titik dua itu anonymous volume, dan dia menjaga node_modules milik image tetap terlihat.
Membaca tulisan mount
Satu baris di volumes: (atau di -v) langsung memberi tahu jenisnya. Cek titik dua dulu, baru sumbernya:
| Tulisan | Jenis | Cirinya |
|---|---|---|
./src:/app |
bind | ada : dan sumbernya path, diawali / atau ./ |
db-data:/var/lib/data |
named | ada : dan sumbernya nama biasa |
/app/node_modules |
anonymous | gak ada :, cuma path di dalam container |
Di Docker sungguhan, sumber yang diawali ../ atau ~ juga bind mount.
down dan down -v
Lab ini menyimpan 3 catatan, lalu menjalankan perintah Compose di ketiga tempat data:
| Data Postgres di | restart |
down lalu up -d |
down -v lalu up -d |
|---|---|---|---|
tanpa volumes: (anonymous volume) |
3 | 0, volume lama tertinggal | 0 |
named volume db-data |
3 | 3 | 0 |
bind mount ./pgdata |
3 | 3 | 3 |
docker compose downmenghapus container dan network. Named volume gak disentuh, jadi catatannya tetap 3. Itu jawaban tebakan pertama.docker compose down -vjuga menghapus named volume yang dideklarasikan di file Compose dan anonymous volume milik container-nya. Catatannya jadi 0. Itu jawaban tebakan kedua.- Folder bind mount gak pernah dihapus Compose. Itu folder milikmu.
$ docker compose down -v
[+] Running 4/4
✔ Container catatan-api-app-1 Removed 0.4s
✔ Container catatan-api-db-1 Removed 0.3s
✔ Volume catatan-api_db-data Removed 0.1s
✔ Network catatan-api_default Removed 0.2s
Log Postgres juga memberi tahu. Waktu folder datanya kosong, dia menyiapkan database baru dan mencetak PostgreSQL init process complete; ready for start up. Waktu datanya masih ada, dia mencetak PostgreSQL Database directory appears to contain a database; Skipping initialization.
⚠️ Jebakan yang sering ketemu
- Database tanpa volume. Jalan lancar berminggu-minggu, lalu satu
downmenghapus semuanya. Selalu pasang named volume untuk data yang gak boleh hilang. down -vjadi kebiasaan. Di laptop berguna untuk mulai dari nol. Di server, satu-vmenghapus database production.- Lupa
./di bind mount.src:/appdibaca sebagai named volume bernamasrc, bukan foldersrc. Compose lalu menolak:service "app" refers to undefined volume src: invalid compose project. - Bind mount
.:/appmenutupinode_modules. App gagal menemukan modulnya. Tambah anonymous volume- /app/node_modules.
TL;DR: Data yang ditulis di dalam container hilang begitu container-nya dihapus (down, docker rm), tapi selamat kalau cuma stop atau restart. Named volume (db-data:/var/lib/postgresql/data, dideklarasikan di volumes: paling bawah) dikelola Docker dan selamat dari down, tapi ikut terhapus oleh down -v. Bind mount (./src:/app atau -v $(pwd):/app) memasang folder host dan gak pernah dihapus Compose, cocok untuk development. Tulisan tanpa titik dua (/app/node_modules) adalah anonymous volume: volume tanpa nama milik satu container. Cara membacanya: gak ada : berarti anonymous, sumber diawali / atau ./ berarti bind, selain itu named.