Docker Volumes & Data — DevOps

Mengapa Perlu Volume? Container bersifat ephemeral — saat dihapus, semua data di dalamnya hilang. Volume memungkinkan data persist di luar lifecycle…

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 menjalankan docker compose down, lalu docker compose up -d. Catatannya masih 3 atau jadi 0? Dan kalau pakai down -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:

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:
$ 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

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 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

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.

Yang akan kamu pelajari