Volume Lifecycle #

Dalam arsitektur container, data memiliki lifecycle yang berbeda dengan container. Container bersifat ephemeral — mudah dibuat dan dihancurkan. Data sering kali perlu bertahan lama, bahkan ketika container mati, di-recreate, atau di-scale.

Inilah kekuatan utama Docker Volume: ia memisahkan lifecycle data dari lifecycle container. Volume ada sebelum container dibuat, ada setelah container dihapus, dan bisa di-mount ke container baru yang dibuat kemudian. Memahami lifecycle volume sangat penting untuk menghindari data loss dan mengelola storage dengan benar.

Artikel ini membahas tuntas lifecycle volume: dari creation, mounting, active use, dangling state, sampai removal. Setelah membaca ini, kamu akan tahu persis apa yang terjadi pada data di setiap tahap, dan bagaimana mengelola volume dengan aman.

Prinsip Dasar: Volume vs Container Lifecycle #

Konsep paling mendasar yang harus kamu pegang:

Volume TIDAK terikat pada lifecycle container.

Artinya:

  • Container bisa dibuat tanpa volume (volume tidak otomatis dibuat).
  • Volume dibuat secara independen dari container.
  • Volume ada sebelum, selama, dan setelah container hidup.
  • Container dihapus TIDAK menghapus volume yang dia mount.
  • Volume hanya bisa dihapus secara eksplisit.
flowchart TB
    subgraph VL["Volume Lifecycle"]
        V1[Created<br/>docker volume create] --> V2[Mounted<br/>container start]
        V2 --> V3[Active<br/>read/write]
        V3 --> V4[Unmounted<br/>container stop/remove]
        V4 --> V5a[Mounted Lagi<br/>container baru]
        V4 --> V5b[Dangling<br/>tidak ada container]
        V5a --> V3
        V5b --> V6[Removed<br/>docker volume rm]
    end
    
    subgraph CL["Container Lifecycle"]
        C1[Created] --> C2[Running]
        C2 --> C3[Stopped/Removed]
        C3 --> C4[*]
    end
    
    VL -.->|independen| CL
    
    style V1 fill:#a8d8a8
    style V2 fill:#a8d8a8
    style V3 fill:#a8d8a8
    style V6 fill:#ff6b6b,color:#fff

Cycle ini menjelaskan banyak hal penting:

  • Container mati bukan berarti data hilang.
  • Container baru bisa mount volume yang sudah ada isinya.
  • Volume yang tidak dipakai menjadi “dangling” — masih ada, tapi tidak terikat.
  • Volume hanya hilang jika kamu secara eksplisit menghapusnya.
Prinsip emas: Container boleh mati, tapi data harus tetap hidup. Volume adalah mekanisme yang menjamin prinsip ini.

Tahapan Lifecycle Volume #

1. Volume Creation (Pembuatan) #

Volume bisa dibuat dengan dua cara: eksplisit atau implisit.

Explicit (Manual) #

docker volume create mydata

Karakteristik:

  • Volume berdiri sendiri, tidak terikat ke container tertentu.
  • Docker membuat direktori di /var/lib/docker/volumes/mydata/_data/.
  • Aman untuk data jangka panjang — volume sudah ada sebelum container pertama.
  • Lebih eksplisit dan terdokumentasi.

Output:

mydata

Implicit (Otomatis oleh Docker) #

docker run -v mydata:/app/data nginx

Jika mydata belum ada, Docker otomatis membuat volume. Karakteristik:

  • Praktis untuk development atau eksperimen cepat.
  • Tidak terdokumentasi — perlu dicek dengan docker volume ls.
  • Rawan dangling volume kalau container sering dibuat dan dihapus.
flowchart TB
    A[docker run -v mydata:/app/data nginx] --> B{Volume mydata<br/>sudah ada?}
    B -- Ya --> C[Mount volume yang ada]
    B -- Tidak --> D[Buat volume otomatis]
    D --> C
    C --> E[Container jalan]

Naming Convention untuk Clarity #

Untuk production, selalu gunakan nama eksplisit dengan konvensi yang jelas:

docker volume create webapp-uploads-prod
docker volume create mysql-data-prod
docker volume create redis-cache-prod

Ini membuat audit dan backup lebih mudah. Kamu bisa langsung tahu volume mana berisi apa.

2. Volume Mounting #

Saat container dijalankan dengan mount volume, Docker me-mount direktori volume ke path di container.

docker run -v mysql-data:/var/lib/mysql mysql:8

Yang terjadi secara internal:

  1. Docker daemon menerima instruksi mount.
  2. Daemon mencari volume mysql-data di direktori /var/lib/docker/volumes/.
  3. Daemon membuat mount point di container (default ke /var/lib/mysql).
  4. Container melihat path ini sebagai direktori biasa.

Container tidak tahu bahwa path-nya adalah volume. Yang container lihat hanya direktori yang bisa dibaca dan ditulis.

flowchart LR
    HOST[Host:<br/>/var/lib/docker/volumes/<br/>mysql-data/_data/] -->|bind mount<br/>level kernel| CONT[Container:<br/>/var/lib/mysql/]
Detail teknis: Docker menggunakan bind mount level kernel untuk me-mount direktori volume ke path di container. Ini bukan copy — saat container membaca /var/lib/mysql/foo.txt, ia langsung membaca file di host. Tidak ada overhead sinkronisasi.

3. Active Use — Volume Sedang Dipakai #

Selama container berjalan, volume berada dalam status active. Di tahap ini:

  • Container bisa baca/tulis data ke mount point.
  • Jika volume di-mount ke banyak container, semua container berbagi data yang sama.
  • Perubahan di satu container langsung terlihat di container lain yang mount volume yang sama.
services:
  app:
    image: my-app
    volumes:
      - shared-data:/app/data

  worker:
    image: my-worker
    volumes:
      - shared-data:/input

Saat app menulis file di /app/data/file.txt, worker bisa langsung membaca file yang sama di /input/file.txt.

flowchart LR
    A[App Container<br/>write] --> VOL[shared-data]
    VOL --> B[Worker Container<br/>read]
    
    A -.->|perubahan langsung| B

Mode Akses: Read-Write vs Read-Only #

Container bisa mount volume dalam dua mode:

  • Read-Write (default) — container bisa baca dan tulis.
  • Read-Only — container hanya bisa baca, tidak bisa tulis.
# Read-write
docker run -v shared-data:/app/data my-app

# Read-only
docker run -v shared-data:/input:ro my-worker

Pattern umum: producer pakai read-write, consumer pakai read-only. Ini memberikan isolasi — consumer tidak bisa merusak data meskipun compromised.

4. Container Stop atau Remove #

docker stop mycontainer
docker rm mycontainer

Yang terjadi:

  • Container hilang.
  • Volume tetap ada dengan semua isinya.
  • Volume mount di-release oleh kernel.

Kamu bisa inspect volume untuk melihat isinya:

docker volume inspect mysql-data

Dan akses langsung dari host:

ls /var/lib/docker/volumes/mysql-data/_data/
flowchart TB
    C1[Container A<br/>running] --> V[Volume<br/>aktif]
    C2[Container B<br/>running] --> V
    
    C1 -.->|stop/remove| GONE[Container A gone]
    C2 --> V
    
    V --> V2[Volume tetap aktif<br/>karena B masih mount]
    
    style GONE fill:#ff6b6b,color:#fff
    style V2 fill:#51cf66,color:#fff

Ini adalah inti lifecycle volume — data tidak hilang saat container hilang.

Pengecualian: Perintah docker compose down -v menghapus volume sekaligus container. Dan docker volume rm nama menghapus volume secara eksplisit. Di luar itu, container lifecycle tidak menghapus volume.

5. Dangling Volume — Volume Tidak Terpakai #

Jika semua container yang mount volume sudah dihapus, volume menjadi dangling:

  • Volume tetap ada di host.
  • Tidak ada container yang mount.
  • Tidak terpakai, tapi belum dihapus.
  • Mengkonsumsi disk space tanpa manfaat.
# Lihat dangling volume
docker volume ls -f dangling=true

Output:

local               my-old-app-data
local               leftover-volume
local               test-volume-abc

Volume-volume ini sering menumpuk di server development atau CI/CD yang sering membuat dan menghapus container.

flowchart TB
    V[Volume] --> C1[Container A]
    V --> C2[Container B]
    
    C1 -.->|hapus| X[Container A removed]
    C2 -.->|hapus| Y[Container B removed]
    
    X --> D[Volume masih ada,<br/>sekarang dangling]
    Y --> D
    
    D --> P[Belum dihapus,<br/>makan disk]
    
    style D fill:#ffd43b
    style P fill:#ffd43b

Dampak Dangling Volume #

  • Disk space terbuang — volume yang tidak terpakai tetap makan storage.
  • Sulit audit — tanpa cleanup, daftar volume jadi membingungkan.
  • Risiko data orphan — volume penting yang lupa di-mount bisa salah di-prune.

Cara Menangani Dangling Volume #

# Lihat dulu yang akan dihapus
docker volume ls -f dangling=true

# Hapus semua dangling volume
docker volume prune

# Hapus dengan konfirmasi otomatis
docker volume prune --force

# Hapus volume yang tidak terpakai + label tertentu
docker volume prune --filter "label=env=staging"
Perhatian: docker volume prune menghapus SEMUA volume yang tidak di-mount ke container (running atau stopped). Jika kamu punya volume penting yang sedang tidak terpakai (mis. service di-scale down), ia akan terhapus. Selalu backup dulu sebelum prune.

6. Volume Removal #

Volume hanya bisa dihapus jika tidak sedang di-mount ke container manapun.

# Hapus satu volume
docker volume rm mysql-data

# Output jika berhasil:
# mysql-data
# Output jika gagal (masih di-mount):
# Error response from daemon: remove mysql-data: volume is in use
# Hapus multiple volume
docker volume rm vol1 vol2 vol3

# Hapus semua yang tidak terpakai
docker volume prune

Yang terjadi saat volume dihapus:

  1. Docker cek apakah volume di-mount ke container manapun.
  2. Jika ya, return error dan tidak hapus.
  3. Jika tidak, Docker hapus direktori volume di host.
  4. Direktori _data dan isinya hilang permanen.
flowchart TB
    A[docker volume rm mydata] --> B{Volume sedang<br/>di-mount?}
    B -- Ya --> C[Error:<br/>volume is in use]
    B -- Tidak --> D[Konfirmasi]
    D --> E[--force?] -- Ya --> F[Hapus]
    E -- Tidak --> D
    F --> G[Data hilang permanen]
    
    style G fill:#ff6b6b,color:#fff

Untuk menghapus volume yang sedang di-mount, kamu harus:

# 1. Stop container yang mount
docker stop container-name

# 2. Remove container
docker rm container-name

# 3. Sekarang volume bisa dihapus
docker volume rm mydata

Atau paksa dengan menghapus container tanpa graceful shutdown:

# Force remove container (auto remove volume juga bisa)
docker rm -f container-name
docker volume rm mydata
Tidak ada undo. Sekali volume dihapus, data di dalamnya hilang. Tidak ada recycle bin, tidak ada recovery tool bawaan. Kalau kamu butuh data, backup dulu.

Lifecycle di Docker Compose #

Di Docker Compose, lifecycle volume sedikit berbeda dari CLI murni.

Volume Dideklarasikan di Compose File #

services:
  db:
    image: postgres:16
    volumes:
      - dbdata:/var/lib/postgresql/data

volumes:
  dbdata:

Lifecycle Compose #

# Buat container + volume
docker compose up -d
# - Container dibuat
# - Volume 'dbdata' dibuat jika belum ada
# - Volume di-mount ke container

# Stop dan hapus container (volume TETAP ADA)
docker compose down
# - Container dihapus
# - Network dihapus
# - Volume TETAP

# Stop + hapus container + hapus volume
docker compose down -v
# - Container dihapus
# - Network dihapus
# - Volume juga DIHAPUS
flowchart LR
    UP[compose up -d] --> RUNNING[Container running<br/>volume mounted]
    DOWN[compose down] --> STOPPED[Container removed<br/>volume TETAP]
    DOWNV[compose down -v] --> GONE[Container removed<br/>volume DIHAPUS]
    
    style GONE fill:#ff6b6b,color:#fff
    style STOPPED fill:#51cf66,color:#fff
Hati-hati dengan compose down -v: Perintah ini menghapus volume yang didefinisikan di compose file. Untuk data production, hampir selalu kamu ingin TIDAK menggunakan flag -v. Volume harus tetap ada setelah down agar data persist.

Inspect Volume dari Compose #

# Lihat semua volume yang didefinisikan
docker compose config --volumes

# Inspect specific volume
docker volume inspect <projectname>_dbdata

Di Compose, nama volume di-prefix dengan nama project (default: nama direktori). Jadi dbdata di compose file menjadi myproject_dbdata di Docker.

Backup Volume dari Compose #

services:
  db:
    image: postgres:16
    volumes:
      - dbdata:/var/lib/postgresql/data

  backup:
    image: alpine
    profiles: ["backup"]    # hanya jalan dengan --profile backup
    volumes:
      - dbdata:/source:ro
      - ./backups:/backup
    command: tar czf /backup/dbdata-$(date +%F).tar.gz -C /source .
# Backup manual
docker compose --profile backup run --rm backup

Pruning Storage — Cleanup Rutin #

Docker menyediakan beberapa command untuk cleanup:

Command Fungsi Bahaya
docker container prune Hapus container stopped Rendah
docker image prune Hapus image tidak terpakai Rendah
docker volume prune Hapus volume dangling Tinggi (data hilang)
docker network prune Hapus network tidak terpakai Rendah
docker system prune Hapus semua yang di atas Tinggi
docker system prune --volumes Hapus termasuk volume Sangat tinggi
# Dry run — lihat apa yang akan dihapus
docker system prune --volumes --dry-run

# Prune dengan filter
docker system prune --volumes --filter "until=24h"  # yang lebih dari 24 jam

Best practice sebelum prune:

  1. Backup volume yang penting.
  2. Inspect daftar yang akan dihapus dengan --dry-run dulu.
  3. Pastikan tidak ada service penting yang stopped.
  4. Test restore dari backup sebelum melakukan prune di production.

Monitoring Volume Lifecycle #

Untuk mengelola volume dengan benar di production, kamu perlu visibility. Berikut alat yang bisa dipakai:

Docker System DF #

# Overall disk usage
docker system df

# Detail per item
docker system df -v

Output:

TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          15        5         2.5GB     1.2GB (48%)
Containers      10        2         500MB     300MB (60%)
Local Volumes   8         3         4.2GB     1.5GB (35%)
Build Cache     5         0         800MB     800MB

Monitoring Script #

#!/bin/bash
# monitoring-volumes.sh
# Jalankan via cron, kirim alert jika volume usage tinggi

# Ambil data volume
docker volume ls -q | while read vol; do
  size=$(docker run --rm -v $vol:/check:ro alpine du -sh /check 2>/dev/null | awk '{print $1}')
  echo "Volume: $vol, Size: $size"
done

# Cek dangling volume
dangling=$(docker volume ls -f dangling=true -q | wc -l)
echo "Dangling volumes: $dangling"

# Alert jika ada volume > 80% usage
# (tergantung setup monitoring kamu)

Integrasi dengan Monitoring Stack #

Untuk production, kirim data volume ke Prometheus/Grafana atau monitoring stack lain:

# docker-compose.yml untuk monitoring
services:
  node-exporter:
    image: prom/node-exporter
    volumes:
      - /var/lib/docker/volumes:/var/lib/docker/volumes:ro
    command:
      - '--path.rootfs=/host'
      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'

Praktik Terbaik Lifecycle Volume #

1. Backup Sebelum Cleanup #

# Backup volume sebelum prune
docker run --rm \
  -v mydata:/source:ro \
  -v /backup:/backup \
  alpine tar czf /backup/mydata-$(date +%F).tar.gz -C /source .

# Baru prune
docker volume prune

2. Naming Convention yang Jelas #

[aplikasi]-[jenis-data]-[environment]

Contoh: webapp-uploads-prod, mysql-data-staging.

3. Dokumentasikan Mount Point #

Setiap volume harus punya catatan:

Volume: pgdata-prod
  Mount point: /var/lib/postgresql/data
  Service: postgres
  Backup: harian ke S3, retensi 30 hari
  Retention: 90 hari (auto-prune setelah itu)
  Restore: ./runbooks/postgres-restore.md

4. Audit Volume Secara Berkala #

# Audit mingguan
docker volume ls
docker volume ls -f dangling=true
docker system df -v

Bersihkan dangling volume secara rutin, tapi backup dulu yang penting.

5. Jangan Asal docker volume prune #

# JANGAN:
docker volume prune --force   # langsung hapus tanpa konfirmasi

# LEBIH BAIK:
docker volume ls -f dangling=true    # lihat dulu
docker volume prune                  # dengan konfirmasi

6. Test Restore secara Berkala #

Volume backup yang tidak pernah di-restore = backup yang tidak ada. Restore ke environment test secara berkala untuk verifikasi.

7. Gunakan Profile Compose untuk Service Sementara #

services:
  db:
    image: postgres:16
    volumes:
      - dbdata:/var/lib/postgresql/data

  backup:
    profiles: ["backup"]   # hanya jalan dengan --profile backup
    image: alpine
    volumes:
      - dbdata:/source:ro
      - ./backups:/backup
    command: tar czf /backup/db.tar.gz -C /source .
# Service utama
docker compose up -d

# Backup manual
docker compose --profile backup run --rm backup

Service backup tidak otomatis jalan saat compose up, hanya saat profile diaktifkan. Ini mencegah backup service yang salah konfigurasi.

8. Monitor Volume Usage #

Setup alert di monitoring system:

  • Volume usage > 80% → warning.
  • Volume usage > 95% → critical.
  • Dangling volume count > 10 → cleanup needed.
  • Volume creation rate abnormal → cek apakah ada service yang salah.

9. Lifecycle Automation #

Untuk environment besar, automate lifecycle management:

# Cron: backup + prune rutin
0 2 * * * /opt/scripts/backup-volumes.sh
0 4 * * 0 /opt/scripts/cleanup-dangling.sh  # mingguan, setelah backup
#!/bin/bash
# cleanup-dangling.sh
# Hapus dangling volume yang sudah di-backup

# Backup dulu
/opt/scripts/backup-volumes.sh

# Tunggu backup selesai
sleep 60

# Cek volume yang tidak punya container
for vol in $(docker volume ls -q); do
  if ! docker ps -a --filter volume=$vol -q | grep -q .; then
    echo "Dangling volume: $vol"
    # Hanya hapus jika punya label tertentu (mis. cleanup-eligible)
    if docker volume inspect $vol --format '{{index .Labels "cleanup-eligible"}}' | grep -q "true"; then
      docker volume rm $vol
    fi
  fi
done

10. Pertimbangkan Volume Driver untuk Auto-Cleanup #

Beberapa volume driver (terutama cloud-native) mendukung TTL dan auto-cleanup:

volumes:
  cache-data:
    driver: amazon/ebs
    driver_opts:
      size: 100
      type: gp3
      # Auto-delete volume saat container berhenti
      # Berguna untuk cache yang boleh hilang

Pola Lifecycle yang Umum #

Pola 1: Database Production #

1. docker compose up -d
   → Container db dibuat
   → Volume pgdata dibuat (pertama kali) atau di-mount
   → Database start, tulis data ke volume

2. Backup harian (cron)
   → pg_dump ke S3

3. Update image (deploy baru)
   → docker compose pull
   → docker compose up -d
   → Container di-recreate
   → Volume pgdata TETAP, database persist

4. Migrasi ke host baru
   → Backup pgdata ke file
   → Copy file ke host baru
   → docker volume create pgdata di host baru
   → Restore backup ke volume
   → docker compose up -d
   → Database jalan dengan data yang sama

Pola 2: Development dengan Ephemeral + Persistent Mix #

services:
  app:
    volumes:
      - ./src:/app/src           # bind mount, source code, ephemeral
      - node_modules:/app/node_modules  # volume, dependency, persistent
      - ./.env:/app/.env:ro      # bind mount, config, read-only
  • Source code — bind mount, langsung terlihat di host, tidak perlu rebuild.
  • node_modules — volume, tidak boleh di-overwrite host, persistent.
  • Config — bind mount read-only, source of truth di host.

Pola 3: Auto-Cleanup Cache #

volumes:
  cache-data:
    labels:
      cleanup-eligible: "true"

Script cleanup mingguan menghapus volume dengan label cleanup-eligible=true yang dangling. Cache yang salah di-prune tidak masalah karena bisa di-rebuild.

Pola 4: Multi-Environment dengan Lifecycle Berbeda #

# docker-compose.prod.yml
services:
  db:
    volumes:
      - pgdata-prod:/var/lib/postgresql/data

volumes:
  pgdata-prod:
    name: myapp-pgdata-prod    # nama eksplisit, tidak di-prefix project
# docker-compose.staging.yml
services:
  db:
    volumes:
      - pgdata-staging:/var/lib/postgresql/data

volumes:
  pgdata-staging:
    name: myapp-pgdata-staging

Production dan staging punya volume dengan nama berbeda, dan bisa di-manage independen. docker compose -f prod.yml down -v tidak akan menghapus volume staging.


Common Mistakes dan Cara Menghindarinya #

1. Menganggap docker rm Menghapus Volume #

# ANTI-PATTERN
docker rm -f container-name
# Developer kira data hilang, padahal volume masih ada
# Cek:
docker volume ls
# Volume masih ada!

Fix: Selalu cek docker volume ls setelah docker rm untuk memastikan cleanup.

2. Lupa Backup Sebelum prune #

# ANTI-PATTERN
docker volume prune --force
# Volume penting yang lupa di-mount langsung hilang

Fix: Selalu backup dulu, atau filter prune hanya untuk label tertentu.

3. compose down -v di Production #

# ANTI-PATTERN
docker compose -f prod.yml down -v
# Semua volume production hilang!

Fix: Hapus -v untuk production. Gunakan down biasa agar volume tetap ada.

4. Tidak Inspect Volume Sebelum Remove #

# ANTI-PATTERN
docker volume rm some-volume
# Developer kira "some-volume" tidak penting
# Ternyata berisi data production

Fix: Selalu inspect dulu:

docker volume inspect some-volume
ls /var/lib/docker/volumes/some-volume/_data/  # cek isinya

5. Tidak Mengetahui Volume Dangling #

Server production penuh setelah beberapa bulan karena dangling volume menumpuk. Tidak ada cleanup rutin.

Fix: Setup monitoring dan cleanup automation.


Ringkasan #

  • Volume lifecycle independen dari container. Container bisa dibuat dan dihapus, volume tetap ada. Ini kekuatan utama Docker Volume — data tidak hilang saat container hilang.
  • Tiga cara membuat volume: explicit (docker volume create), implicit (saat docker run -v nama:/path), atau via Compose. Untuk production, selalu gunakan explicit dengan nama yang jelas.
  • Empat status volume: Created (baru dibuat, belum di-mount), Mounted (container mount volume), Active (ada proses baca/tulis), Unmounted (tidak ada container yang mount).
  • Dangling volume = volume yang tidak di-mount ke container manapun. Masih makan disk, tapi tidak berguna. Cleanup dengan docker volume prune, tapi BACKUP DULU.
  • Volume removal hanya bisa dilakukan jika volume tidak di-mount. docker volume rm gagal dengan error “volume is in use” jika masih dipakai. Stop dan hapus container dulu, atau paksa dengan -f.
  • docker compose down TIDAK menghapus volume. docker compose down -v yang menghapus. Untuk production, hampir selalu kamu ingin down tanpa -v.
  • Naming convention yang konsisten memudahkan audit, backup, dan cleanup. Pola: [aplikasi]-[jenis-data]-[environment], mis. webapp-uploads-prod.
  • Monitoring: docker system df dan docker system df -v menunjukkan penggunaan storage. Setup alert untuk volume usage tinggi dan dangling volume yang menumpuk.
  • Best practice: backup sebelum prune, audit volume rutin, dokumentasikan mount point, test restore berkala, gunakan profile Compose untuk service backup, monitor usage, dan automate cleanup dengan hati-hati.
  • Aturan emas: Volume boleh ada tanpa container, container boleh ada tanpa volume, tapi volume HANYA bisa hilang karena dihapus eksplisit. Inilah yang membuat data persist di dunia container.

← Sebelumnya: Volume   Berikutnya: Bind Mount →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact