Persistent Data #

Salah satu kesalahpahaman paling umum ketika mulai menggunakan Docker adalah menganggap container sebagai tempat penyimpanan data. Secara default, container bersifat ephemeral: ketika container dihapus, semua data di dalam filesystem-nya ikut hilang tanpa jejak. Ini bukan bug — ini adalah desain fundamental.

Tapi aplikasi nyata membutuhkan data yang bertahan. Database harus menyimpan transaksi, aplikasi harus menyimpan file upload, dan log aplikasi harus bisa diakses setelah container restart. Di sinilah konsep persistent data menjadi sangat penting.

Persistent data adalah mekanisme untuk menyimpan data di luar lifecycle container — di tempat yang tidak ikut hilang saat container mati, di-restart, atau di-recreate. Artikel ini membahas tuntas tiga mekanisme resmi yang disediakan Docker: volume, bind mount, dan tmpfs. Masing-masing punya karakteristik, use case, dan trade-off yang berbeda.

Memilih mekanisme yang tepat adalah keputusan arsitektur yang akan menentukan stabilitas, portabilitas, dan keamanan aplikasi kamu ke depan. Mari kita bahas dari fondasi.

Mengapa Container Tidak Bisa Menyimpan Data #

Sebelum masuk ke solusi, penting paham dulu mengapa masalah ini ada. Ini bukan kelemahan Docker — ini adalah konsekuensi langsung dari cara container bekerja.

Container adalah Layer, Bukan Disk #

Seperti yang sudah dibahas di artikel sebelumnya, setiap container adalah writable layer di atas image layer read-only. Writable layer ini:

  • Hidup di host, tapi dikelola oleh Docker.
  • Terikat pada container — saat container dihapus, layer ini ikut hilang.
  • Beroperasi dengan copy-on-write — file hanya disalin ke layer ini saat ada perubahan.
flowchart TB
    subgraph IMG["Image Layers (read-only)"]
        L1[Base]
        L2[RUN]
        L3[COPY]
    end
    
    subgraph CONT["Container Writable Layer"]
        W[Perubahan runtime]
    end
    
    IMG --> CONT
    
    RM[docker rm container] -.->|hapus| CONT
    
    style CONT fill:#ffd8a8
    style RM fill:#ff6b6b,color:#fff

Saat kamu menjalankan docker rm container, Docker menghapus writable layer ini. Semua data yang ditulis aplikasi ke filesystem selama container hidup — file database, log, upload — ikut lenyap. Tidak ada cara untuk recover.

Dampak pada Aplikasi Nyata #

Bayangkan kamu menjalankan MySQL di container tanpa mekanisme persistence:

docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=secret mysql:8

Aplikasinya jalan. Kamu buat database, isi tabel, simpan data. Semuanya bekerja seperti biasa. Lalu suatu saat, kamu restart container (mis. karena update image):

docker rm -f mysql
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=secret mysql:8

Database baru, kosong. Semua data yang kamu simpan hilang. Tidak ada error, tidak ada warning — container bekerja sempurna, tapi datanya musnah.

Ini bukan skenario hipotetis. Ini adalah sumber data loss paling umum di Docker, dan penyebabnya selalu sama: data disimpan di writable layer, bukan di tempat yang persisten.

Aturan emas Docker: Container = stateless, data = stateful di luar container. Selalu. Tanpa kecuali. Ini bukan best practice yang bisa dinegosiasikan — ini adalah cara kerja Docker.

Tiga Mekanisme Persistent Data di Docker #

Docker menyediakan tiga mekanisme resmi untuk menyimpan data di luar lifecycle container. Masing-masing punya karakteristik, kelebihan, dan trade-off yang berbeda.

flowchart TB
    P[Persistent Data di Docker] --> V[Volume<br/>dikelola Docker]
    P --> B[Bind Mount<br/>path host manual]
    P --> T[tmpfs<br/>di RAM]
    
    V --> V1[Production]
    V --> V2[Database]
    V --> V3[Multi-container sharing]
    
    B --> B1[Development]
    B --> B2[Live reload]
    B --> B3[Config file]
    
    T --> T1[Cache sensitif]
    T --> T2[Session]
    T --> T3[Secret sementara]
Mekanisme Lokasi Fisik Persisten? Managed By Use Case
Volume /var/lib/docker/volumes/ Ya Docker Production, database, multi-container
Bind Mount Path host sesuai argumen Ya (tergantung host) User / OS Development, live reload, config
tmpfs RAM Tidak OS Cache sensitif, secret sementara

Ketiganya dipasang ke container dengan flag -v atau --mount saat docker run, atau di section volumes di docker-compose.yml. Mari kita bahas masing-masing.


Volume — Mekanisme Resmi untuk Production #

Volume adalah mekanisme persistent data yang dikelola langsung oleh Docker Engine. Ini adalah pilihan yang direkomendasikan untuk hampir semua skenario production.

Karakteristik Volume #

  • Disimpan di host dalam direktori yang dikelola Docker (default /var/lib/docker/volumes/).
  • Nama volume bisa dipilih user (named volume) atau random (anonymous volume).
  • Lifecycle independen dari container — volume tetap ada saat container dihapus.
  • Bisa di-mount ke banyak container sekaligus (sharing).
  • Mendukung volume driver untuk integrasi dengan storage eksternal (NFS, EBS, cloud storage).
  • Portabel — bisa di-backup, di-restore, dan di-migrate ke host lain.

Cara Kerja #

flowchart LR
    subgraph HOST["Host Machine"]
        HV[/var/lib/docker/volumes/<br/>nama-volume/_data/]
    end
    
    subgraph C1["Container A"]
        CA[/data/]
    end
    
    subgraph C2["Container B"]
        CB[/var/lib/mysql/]
    end
    
    HV --> CA
    HV --> CB

Container tidak pernah tahu lokasi fisik volume di host. Yang container lihat hanya mount point di filesystem internal. Ini decoupling yang membuat volume jadi portable dan aman.

Membuat dan Menggunakan Volume #

Cara paling eksplisit:

# Buat volume
docker volume create mysql-data

# Jalankan container dengan volume
docker run -d \
  --name mysql \
  -v mysql-data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=secret \
  mysql:8

Cara implisit (Docker otomatis buat):

# Docker otomatis membuat volume "app-data" karena belum ada
docker run -d \
  --name app \
  -v app-data:/app/data \
  my-app

Verifikasi volume:

# Lihat semua volume
docker volume ls

# Inspect detail volume
docker volume inspect mysql-data

Output inspect:

[
  {
    "Name": "mysql-data",
    "Driver": "local",
    "Mountpoint": "/var/lib/docker/volumes/mysql-data/_data",
    "CreatedAt": "2026-06-06T12:00:00Z",
    "Labels": {},
    "Scope": "local"
  }
]

Volume di Docker Compose #

Di Compose, volume didefinisikan secara deklaratif:

version: "3.9"
services:
  db:
    image: mysql:8
    environment:
      MYSQL_ROOT_PASSWORD: secret
    volumes:
      - mysql-data:/var/lib/mysql

  backup:
    image: alpine
    volumes:
      - mysql-data:/source:ro
      - ./backups:/backup
    command: tar czf /backup/db.tar.gz -C /source .

volumes:
  mysql-data:

Keuntungan deklaratif:

  • Reproducibledocker compose up selalu membuat volume dengan konfigurasi yang sama.
  • Self-documenting — baca docker-compose.yml, langsung tahu volume apa yang dipakai.
  • Multi-service sharing — volume didefinisikan sekali, dipakai banyak service.
Best practice: Selalu gunakan named volume (mysql-data, app-uploads, cache-data) daripada anonymous volume (random hash). Named volume lebih mudah diidentifikasi, di-backup, dan di-manage.

Bind Mount — Path Host ke Container #

Bind mount adalah mekanisme yang menghubungkan file atau direktori spesifik di host langsung ke path di container. Tidak ada abstraksi — apa yang ada di path host adalah yang ada di path container.

Karakteristik Bind Mount #

  • Path host ditentukan manual (path absolut atau relatif terhadap docker-compose.yml).
  • Tidak dikelola Docker — file di host bisa diakses langsung tanpa Docker.
  • Bergantung pada struktur filesystem host — tidak portabel.
  • Perubahan di host langsung terlihat di container (tanpa copy).
  • Sangat cocok untuk development (live reload) dan akses file konfigurasi.

Cara Kerja #

flowchart LR
    subgraph HOST["Host Machine"]
        HP[/home/dev/project/src/]
        HC[/etc/nginx/nginx.conf]
    end
    
    subgraph CONT["Container"]
        CP[/app/src/]
        CC[/etc/nginx/nginx.conf]
    end
    
    HP <--> CP
    HC <--> CC
    
    style HP fill:#a8d8a8
    style CP fill:#ffd8a8

Bedanya dengan volume:

  • Volume → Docker yang pilih path di host, container tidak tahu.
  • Bind mount → User yang pilih path di host, harus konsisten di semua environment.

Cara Penggunaan #

CLI dengan format -v:

docker run -d \
  --name dev-app \
  -v /home/dev/project/src:/app/src \
  my-app:dev

CLI dengan format --mount (lebih eksplisit):

docker run -d \
  --name dev-app \
  --mount type=bind,source=/home/dev/project/src,target=/app/src \
  my-app:dev

Format --mount lebih verbose tapi lebih jelas — cocok untuk script dan CI.

Docker Compose (path relatif terhadap file compose):

services:
  app:
    image: my-app:dev
    volumes:
      - ./src:/app/src          # bind mount, path relatif
      - ./nginx.conf:/etc/nginx/nginx.conf:ro  # read-only

Read-Only Bind Mount #

Untuk file konfigurasi yang tidak boleh diubah container, tambahkan :ro:

docker run -d \
  --name nginx \
  -v ./nginx.conf:/etc/nginx/nginx.conf:ro \
  nginx

Container bisa baca, tapi tidak bisa tulis. Ini pola umum untuk konfigurasi — host adalah source of truth, container hanya konsumen.

Detail teknis: :ro di bind mount adalah deep read-only — semua file di dalam path menjadi read-only, tidak bisa ditulis oleh container. Ini berbeda dengan volume biasa yang bisa read-write.

tmpfs — Storage di Memory #

tmpfs menyimpan data langsung di RAM (memory), bukan di disk. Ini memberikan kecepatan akses paling tinggi, tapi data hilang saat container berhenti.

Karakteristik tmpfs #

  • Disimpan di memory host (RAM).
  • Sangat cepat — kecepatan mendekati native memory access.
  • Tidak persisten — hilang saat container berhenti.
  • Tidak tersentuh filesystem disk host.
  • Cocok untuk data sensitif (tidak pernah ditulis ke disk) atau data sementara.

Use Case #

  • Cache — data yang boleh hilang, tapi harus cepat diakses.
  • Session storage — session web yang tidak perlu bertahan lama.
  • Secret sementara — token, API key yang di-load saat startup tapi tidak perlu di-persist.
  • Data sensitif — informasi yang tidak boleh meninggalkan memory (compliance tertentu).

Cara Penggunaan #

docker run -d \
  --name app \
  --tmpfs /app/cache:size=100m \
  my-app

Flag --tmpfs menerima opsi size (batas memory) dan mode (permission).

Di Docker Compose:

services:
  app:
    image: my-app
    tmpfs:
      - /app/cache:size=100m

Kapan TIDAK Menggunakan tmpfs #

  • Database — data harus persist, dan memory bisa habis.
  • File upload user — harus tahan lama.
  • Production cache yang mahal di-rebuild — kalau butuh waktu lama untuk regenerate, simpan di volume.

Perbandingan Lengkap Ketiga Mekanisme #

Aspek Volume Bind Mount tmpfs
Lokasi /var/lib/docker/volumes/ Path host manual RAM
Persisten Ya Ya (tergantung host) Tidak
Managed by Docker User / OS OS
Portabilitas Tinggi Rendah N/A
Performa Cepat (native FS) Sangat cepat (no abstraction) Paling cepat (RAM)
Sharing antar container Ya Ya Ya
Backup/migration Mudah Harus copy manual Tidak perlu
Cocok untuk production Ya Jarang Untuk cache saja
Cocok untuk development Ya Ya Kadang
Akses dari host Harus via Docker Ya, langsung Tidak

Decision Tree #

flowchart TD
    A{Data harus<br/>bertahan<br/>setelah<br/>container mati?}
    A -- Tidak --> B[tmpfs]
    A -- Ya --> C{Container<br/>akan di-deploy<br/>di banyak host?}
    C -- Ya --> D[Volume]
    C -- Tidak --> E{Pakai path<br/>host tertentu<br/>untuk live reload<br/>atau config?}
    E -- Ya --> F[Bind Mount]
    E -- Tidak --> D[Volume]

Prinsip Separation of Concern #

Inti dari semua mekanisme di atas adalah satu prinsip arsitektur: pemisahan antara aplikasi dan data.

flowchart TB
    subgraph APP["Application Layer (Stateless)"]
        A1[Container A]
        A2[Container B]
        A3[Container C]
    end
    
    subgraph DATA["Data Layer (Stateful)"]
        D1[Volume]
        D2[Database Service]
        D3[Object Storage]
    end
    
    APP -->|mount / akses| DATA
    
    style APP fill:#a8d8a8
    style DATA fill:#ffd8a8
  • Application layer = container, image, kode. Stateless, bisa di-replace kapan saja.
  • Data layer = volume, database, object storage. Persistent, di-manage terpisah.

Prinsip ini bukan hanya tentang Docker — ini adalah fondasi arsitektur cloud-native secara umum. Microservices, twelve-factor app, dan cloud-native architecture semuanya berangkat dari sini.

Twelve-Factor App, Factor VI: “Execute the app as one or more stateless processes.” Artinya aplikasi tidak boleh menyimpan state di prosesnya. State harus disimpan di backing service — yang di konteks Docker adalah volume atau external database.

Kapan Persistent Data TIDAK Diperlukan #

Tidak semua workload butuh persistent data. Ada situasi di mana ephemeral benar-benar yang kamu mau:

  • CI/CD build job — hasil build biasanya di-push ke registry, tidak perlu di-persist di container.
  • Unit test container — test passing/failing cukup di stdout, tidak perlu simpan data.
  • Batch processing satu kali — output ditulis ke stdout atau ke host via volume, lalu container dihapus.
  • Stateless microservice — service yang hanya memproses request tanpa menyimpan state.

Untuk workload seperti ini, tidak perlu volume atau bind mount. Cukup biarkan container hidup singkat dan mati.


Kapan Persistent Data WAJIB Ada #

Beberapa workload tidak bisa hidup tanpa persistent data:

  • Database (MySQL, PostgreSQL, MongoDB) — data harus bertahan.
  • File upload user — gambar, dokumen, attachment.
  • Message queue persistent — Kafka, RabbitMQ dengan durable queue.
  • Search index — Elasticsearch, OpenSearch.
  • Cache yang mahal di-rebuild — Redis dengan persistence (RDB/AOF), atau cache hasil komputasi.
  • Log aplikasi — terutama jika di-aggregasi dari banyak container.
  • ML model artifact — model yang sudah di-train dan di-load saat container start.

Untuk semua ini, tanpa persistent data, kamu akan kehilangan data setiap kali container di-restart. Ini bukan skenario yang bisa diterima di production.


Pola Umum Penggunaan #

1. Database dengan Volume #

services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: secret
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

Pola paling standar. Data Postgres di-mount ke volume, container bisa di-restart tanpa kehilangan data.

2. Multi-Container dengan Shared Volume #

services:
  app:
    image: my-app
    volumes:
      - uploads:/app/uploads
  processor:
    image: image-processor
    volumes:
      - uploads:/input
    depends_on:
      - app

volumes:
  uploads:

app menulis file ke /app/uploads, processor membaca dari /input. Keduanya mount volume yang sama.

3. Development dengan Bind Mount + Volume Hybrid #

services:
  app:
    build: .
    volumes:
      - ./src:/app/src          # bind mount untuk live reload
      - node_modules:/app/node_modules  # volume untuk dependency
      - ./.env:/app/.env:ro     # bind mount untuk config
    ports:
      - "3000:3000"

volumes:
  node_modules:

Bind mount untuk source code (butuh live reload), volume untuk node_modules (supaya tidak di-overwrite oleh host), dan bind mount read-only untuk .env (config tidak boleh diubah container).

4. tmpfs untuk Secret #

services:
  app:
    image: my-app
    environment:
      - API_KEY_FILE=/run/secrets/api_key
    secrets:
      - api_key
    tmpfs:
      - /run/secrets:size=1m,mode=0700

secrets:
  api_key:
    file: ./secrets/api_key.txt

Secret di-load ke tmpfs saat start, disimpan di memory, tidak pernah ditulis ke disk.


Best Practice Persistent Data #

1. Gunakan Volume untuk Production #

Volume adalah pilihan default untuk hampir semua skenario production. Managed by Docker, portabel, aman.

2. Gunakan Bind Mount Hanya untuk Development #

Bind mount cocok untuk live reload source code dan akses config. Untuk data production, gunakan volume.

3. Jangan Simpan Data Penting di Container #

Ini sudah jadi aturan emas, tapi masih sering dilanggar. Selalu pikirkan: “kalau container dihapus sekarang, data apa yang hilang?”

4. Pisahkan Data, Config, dan Log #

volumes:
  - app-data:/app/data           # data aplikasi
  - app-config:/app/config       # konfigurasi
  - app-logs:/app/logs           # log (atau pakai logging driver)

Jangan gabungkan semuanya di satu volume — sulit di-backup dan di-migrate.

5. Backup Volume Secara Berkala #

Volume bukan backup. Ia hanya memindahkan lifecycle data ke host. Untuk data penting, kamu tetap butuh backup ke lokasi lain (S3, NFS, tape).

6. Gunakan tmpfs untuk Data Sensitif #

API key, session token, dan data yang tidak boleh meninggalkan memory harus disimpan di tmpfs.

7. Monitor Penggunaan Disk #

Volume bisa membengkak. Pantau dengan docker system df dan docker volume ls -f dangling=true.

8. Dokumentasikan Mount Point #

Setiap volume harus punya nama yang jelas dan didokumentasikan. Tim developer yang baru onboard harus bisa langsung tahu volume mana yang berisi apa.


Ringkasan #

  • Container Docker bersifat ephemeral — semua data di writable layer hilang saat container dihapus. Ini bukan bug, ini desain. Persistent data adalah mekanisme untuk menyimpan data di luar lifecycle container.
  • Tiga mekanisme resmi: Volume (managed by Docker, untuk production), Bind Mount (path host manual, untuk development), tmpfs (di RAM, untuk cache sensitif). Masing-masing punya trade-off yang berbeda.
  • Volume adalah pilihan default untuk production. Dikelola Docker, portabel, mendukung volume driver untuk integrasi storage eksternal (NFS, EBS, cloud), dan bisa di-share antar container.
  • Bind Mount menghubungkan path host langsung ke container. Cocok untuk development (live reload source code) dan config file. Tidak portabel karena bergantung pada struktur host.
  • tmpfs menyimpan data di memory. Sangat cepat tapi tidak persisten. Cocok untuk cache, session, dan secret sementara yang tidak boleh ditulis ke disk.
  • Prinsip utama: separation of concern — application layer stateless, data layer stateful di luar container. Ini fondasi arsitektur cloud-native dan twelve-factor app.
  • Wajib pakai persistent data untuk: database, file upload, search index, message queue durable, log aplikasi, dan ML model artifact. Tidak perlu untuk: CI/CD build, unit test, batch processing sekali jalan.
  • Best practice: named volume untuk production, bind mount untuk development, tmpfs untuk data sensitif, pisahkan data/config/log, backup volume berkala, dan monitor penggunaan disk.
  • Aturan emas: Container = stateless, data = stateful di luar container. Selalu.

← Sebelumnya: Ephemeral Container   Berikutnya: Data Loss →

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