Empat mesin database kini berdiri berdampingan di mesin kita, dan masing-masing sudah menanam datanya: PostgreSQL menyimpan Kopi Torabika di volume db-data, MySQL dan MariaDB masing-masing punya katalognya, Redis menyimpan kunci-kuncinya di cache-data. Sepanjang section ini kita mengulang satu klaim seperti mantra: pasang volume, dan data akan hidup lebih lama dari container. Klaim itu sebenarnya sudah kita buktikan sekilas dengan berkas halo.txt pada Chapter Volume dan dengan siklus down/up pada Chapter Volume dan Bind Mount — tetapi untuk database, pembuktian setengah hati tidak cukup. Chapter ini mengadili klaim itu sampai tuntas: kita mengisi database, membunuh containernya secara paksa, membangkitkannya dari nol, lalu membedah volume sampai ke berkas fisiknya untuk melihat di mana sebenarnya data itu tinggal. Chapter ini juga merapikan disiplin di sekelilingnya — volume anonim yang menumpuk diam-diam, flag down -v yang menghapus segalanya, dan ritual reset bersih untuk eksperimen berikutnya.
Demo: Tabel yang Selamat dari docker rm -f #
Panggungnya tetap proyek katalog. Container db dari chapter pertama masih hidup dengan tabel produk; kita tambahkan satu tabel lagi bernama peserta berisi dua nama:
$ docker exec db psql -U katalog -d katalog -c "CREATE TABLE peserta (nama TEXT);"
$ docker exec db psql -U katalog -d katalog -c "INSERT INTO peserta VALUES ('Budi'), ('Siti');"Output yang Dihasilkan:
CREATE TABLE
INSERT 0 2Database katalog kini memegang dua tabel dan tiga baris. Sekarang datang terdakwanya — perintah yang paling ditakuti pemilik data:
$ docker rm -f dbOutput yang Dihasilkan:
dbSatu baris db adalah seluruh obituari container itu: writable layer, environment, nama, jaringan — semuanya lenyap. Lalu kita jalankan ulang perintah yang sama, dengan volume yang sama:
$ docker run -d --name db \
-e POSTGRES_USER=katalog \
-e POSTGRES_PASSWORD=rahasia123 \
-e POSTGRES_DB=katalog \
-v db-data:/var/lib/postgresql/data \
postgres:18-alpineOutput yang Dihasilkan:
c60cb7904d5887844319187b7dc024c68a9c361fbf6ce81441bfb8481a569cdbID container yang baru — container ini memang makhluk baru. Log startup-nya membocorkan apa yang dilihat entry point di dalam volume:
$ docker logs dbOutput yang Dihasilkan:
PostgreSQL Database directory appears to be a database; Skipping initialization
...
2026-10-13 21:44:18.402 UTC [1] LOG: starting PostgreSQL 18.4 (Debian 18.4-1.pgdg130+1) on x86_64-pc-linux-musl, compiled by gcc (Alpine 14.2.0) 14.2.0, 64-bit
2026-10-13 21:44:18.403 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432
2026-10-13 21:44:18.414 UTC [1] LOG: database system is ready to accept connectionsSkipping initialization — kalimat terpenting pada log itu. Dan kini saatnya pembuktian terakhir:
$ docker exec db psql -U katalog -d katalog -c "SELECT * FROM peserta;"
$ docker exec db psql -U katalog -d katalog -c "\dt"Output yang Dihasilkan:
nama
------
Budi
Siti
(2 rows)
List of relations
Schema | Name | Type | Owner
--------+---------+-------+---------
public | peserta | table | katalog
public | produk | table | katalog
(2 rows)Mari kita telaah apa yang barusan terjadi:
docker rm -fmengirim sinyal SIGKILL — container dipotong tanpa masa tenggang; seluruh writable layer beserta tulisannya dihapus dari disk host. Bila datanya menumpang di layer itu, tabelpesertaseharusnya sudah mati.- Volume tidak ikut terpanggil —
db-dataadalah objek terpisah yang hidup di luar container;docker rmtidak pernah menyentuhnya, sehingga cluster PostgreSQL di dalamnya tetap utuh menunggu penghuni baru. Skipping initializationadalah kunci — entry point mendapati direktori data berisi cluster utuh, jadiinitdbdilewati; penyalaan jauh lebih cepat dan tidak satu variabelPOSTGRES_*pun diproses ulang.- Variabel dijalankan ulang pun tidak berbekas — kata sandi yang sama kita kirim ulang pada run kedua, dan PostgreSQL mengabaikannya; inisialisasi hanya terjadi pada volume kosong, aturan yang kita catat sejak chapter pertama section ini.
- Kedua tabel selamat —
pesertayang barusan kita tanam sekaligusprodukberisi Kopi Torabika dari chapter pertama; data lintas container, lintas waktu, tetap setia menunggu.
Mekanisme: Cluster Hidup di Volume #
Bukti di atas barusan memperlihatkan hasil; mekanismya terbaca dari metadata volume itu sendiri:
$ docker volume inspect db-dataOutput yang Dihasilkan:
[
{
"CreatedAt": "2026-10-13T20:41:16.812439857Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/db-data/_data",
"Options": null,
"Scope": "local"
}
]Mari kita telaah dua field yang paling bicara:
CreatedAt20:41 — volume ini lahir pada chapter pertama section ini, saatdocker runpertama memasangkannya; umurnya sudah melewati satu kematian container dan akan terus berlari melewati kematian berikutnya.Mountpoint— alamat fisik di disk host tempat seluruh isi/var/lib/postgresql/databenar-benar ditulis; area/var/lib/dockeradalah wilayah sang daemon, dan jalur resmi kita ke isinya adalah pemasangan volume, bukan mencomot berkasnya secara langsung.
Alur lengkap pemisahan umur itu bisa kita petakan sebagai satu diagram:
Mari kita telaah diagram di atas: sisi kiri bawah adalah nasib container — layer dan identitasnya benar-benar musnah; sisi kanan bawah adalah nasib data — beralamat tetap di mountpoint volume, tidak tersentuh putaran hidup apa pun di atasnya. Seluruh mekanisme volume bernama ini adalah materi Chapter Volume; yang baru di sini hanyalah buktinya kini berbentuk tabel SQL, bukan sekadar berkas teks.
Volume Bernama versus Volume Anonim #
Kemenangan kita bertumpu pada satu kata: nama. Volume db-data bisa dirujuk ulang oleh docker run mana pun karena namanya ditulis manusia. Kelas volume yang lain tidak seberuntung itu — dan image database kita justru produsen terbesannya:
Daftar volume di mesin cerita kita saat ini memperlihatkan sehatnya kebiasaan menamai:
$ docker volume lsOutput yang Dihasilkan:
DRIVER VOLUME NAME
local cache-data
local data-web
local db-data
local db-data-maria
local db-data-mysql
local katalog_db-data
local latihan-volume
local pgdataDelapan baris itu adalah seluruh penduduk mesin cerita kita pada malam ini; di mesin pembaca, volume milik stack lain yang pernah dibuat juga akan ikut tampil di daftar yang sama. Mari kita telaah namanya satu per satu: db-data, db-data-mysql, db-data-maria, dan cache-data adalah empat penduduk section ini; katalog_db-data adalah volume Compose milik proyek katalog dengan prefix project yang sudah kita kenal; data-web, pgdata, dan latihan-volume adalah kenalan lama Section Penyimpanan Data. Tidak ada satu pun baris hex — bukti bahwa seluruh jalur yang kita lalui memasang volume secara eksplisit.
down -v di Compose adalah Factory Reset #
Di dunia docker run, bencana data datang dari lupa memasang volume. Di dunia Compose, lupa semacam itu nyaris mustahil — volume dideklarasikan di dalam compose.yaml, dan kita sudah membuktikan pada Section Docker Compose bahwa docker compose down membuang container dan network saja sambil membiarkan katalog_db-data tetap berdiri. Akan tetapi Compose menyimpan satu pintu yang jauh lebih berbahaya daripada sekadar lupa: flag -v pada perintah bongkar itu. Output berikut adalah kenyataan yang sudah kita saksikan pada Chapter Volume dan Bind Mount:
$ docker compose down -vOutput yang Dihasilkan:
[+] Running 4/4
✔ Container katalog-web-1 Removed
✔ Container katalog-db-1 Removed
✔ Network katalog_default Removed
✔ Volume katalog_db-data RemovedBerkas Fisik di Balik Mountpoint #
Bukti terakhir turun satu lapisan lagi: ke berkas-berkas yang benar-benar tersimpan di volume. Kita pinjam pola helper container dari Chapter Backup dan Restore Volume — container sekali jalan yang memasang volumenya tanpa menyentuh wilayah daemon secara langsung:
$ docker run --rm -v db-data:/data alpine:3.20 ls /dataOutput yang Dihasilkan:
PG_VERSION
base
global
pg_hba.conf
pg_ident.conf
pg_logical
pg_multixact
pg_notify
pg_replslot
pg_serial
pg_snapshots
pg_stat
pg_stat_tmp
pg_tblspc
pg_twophase
pg_wal
postgresql.auto.conf
postgresql.confInilah isi rumah sang data — struktur yang persis sama dengan instalasi PostgreSQL di server fisik mana pun. Mari kita telaah anggota-anggotanya yang paling penting:
base— direktori tempat data setiap database sungguhan disimpan; tabelprodukdanpesertahidup di dalam berkas-berkasnya.pg_wal— write-ahead log, jurnal ketahanan PostgreSQL; setiap perubahan dicatat ke sini lebih dulu sebelum diterapkan, dan inilah yang membuat database sanggup pulih dari mati mendadak — termasuk SIGKILL daridocker rm -ftadi.postgresql.conf,pg_hba.conf— konfigurasi cluster beserta aturan autentikasinya; mengubah perilaku server pada akhirnya adalah mengubah berkas-berkas ini.PG_VERSION— berkas satu baris berisi angka versi mayor cluster. Kita bacakan isinya:
$ docker run --rm -v db-data:/data alpine:3.20 cat /data/PG_VERSIONOutput yang Dihasilkan:
18Angka 18 itu bukan sekadar hiasan: server PostgreSQL dari versi mayor yang berbeda menolak menyala di atas cluster yang tidak cocok versinya. Inilah alasan mengapa pergantian tag image seperti postgres:18-alpine ke seri mayor berbaru tidak bisa dilakukan dengan asal ganti — jalan resminya menanti Chapter Migrasi Versi Database di ujung section ini.
Ukuran Volume dan Jejak Disk #
Empat volume section ini menyimpan ratusan megabyte data, dan Docker punya pembukunya. Ringkasan pemakaian disk terbaca lewat docker system df:
$ docker system dfOutput yang Dihasilkan:
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 10 4 1.89GB 405MB (21%)
Containers 4 4 108B 0B (0%)
Local Volumes 8 4 379.9MB 0B (0%)
Build cache 11 0 84.2MB 84.2MBAngka-angka ini milik mesin cerita kita dan akan berbeda di mesin pembaca. Mari kita telaah baris yang menjadi urusan kita:
Local Volumes 8— delapan volume yang kita lihat padadocker volume ls;ACTIVE 4berarti tepat empat sedang dipasang container hidup:db-data,db-data-mysql,db-data-maria, dancache-data.SIZE 379.9MB— penjumlahan isi seluruh volume; porsi terbesarnya datang dari direktori data MySQL yang boros sejak lahir, disusul cluster PostgreSQL, sementaracache-datasi Redis hanya menyumbang ratusan kilobyte.RECLAIMABLE 0B— tidak ada volume anonim terlantar; volume bernama yang sedang tidak dipakai pun tidak dihitung sebagai sampah oleh pembersihan bawaan — selaras dengan prinsip yang sudah kita pegang: yang bernama dianggap milik.- Flag
-vuntuk detail per volume —docker system df -vmemecah baris Local Volumes menjadi tabel per volume lengkap dengan nama dan ukurannya, sehingga kita bisa mengetahui persis berapa jatahdb-datatanpa menebak dari total.
Kapan Data Tidak Persisten #
Volume bukan satu-satunya tempat container menulis, dan hanya satu jenis alamat yang menjanjikan umur panjang. Tabel berikut merangkum seluruh skenario yang telah kita saksikan sepanjang course:
| Skenario penulisan data | Persisten? | Alasan |
|---|---|---|
Volume bernama, container dihapus (docker rm -f) | ya | volume adalah objek terpisah dari container |
Volume bernama, stack dibongkar (docker compose down) | ya | down hanya membuang container dan network |
| Writable layer tanpa mount | tidak | layer hidup selama container itu saja — Chapter Writable Container Layer |
| tmpfs mount | tidak | isi hidup di memori dan lenyap saat container berhenti — Chapter tmpfs Mount |
| Container baru dengan volume anonim baru | tidak | volume lama masih ada tapi tak terjangkau tanpa nama |
docker compose down -v | tidak | volume sengaja dibuang bersama stack |
Empat baris terakhir adalah para tersangka klasik di balik laporan “data Docker saya hilang sendiri” — dan keempatnya bukan ulah Docker yang jahat, melainkan alamat yang salah dipilih sejak awal.
Reset Bersih dan Pembersihan Arena #
Eksperimen database yang bisa diulang menuntut ritual reset yang jujur, dan bentuk lengkapnya hanya dua perintah dengan urutan yang tidak bisa ditukar — pola yang kita simpan untuk setiap kali ingin menyaksikan ulang tarian inisialisasi first-run dengan variabel yang baru:
$ docker rm -f db && docker volume rm db-dataUrutannya wajib: docker volume rm menolak membuang volume yang masih dipasang container, jadi containernya harus pergi lebih dulu — pagar kecil yang menyelamatkan kita dari salah ketik sesaat sebelum bencana. Kita tidak menjalankan ritual itu sekarang — db-data justru menjadi pusat pembuktian chapter ini — tetapi pola inilah yang kita pakai untuk membereskan tiga mesin lain yang sudah menuntaskan tugas peragaannya:
$ docker rm -f db-mysql db-maria cache
$ docker volume rm db-data-mysql db-data-maria cache-dataOutput yang Dihasilkan:
db-mysql
db-maria
cache
db-data-mysql
db-data-maria
cache-dataSementara itu db dan db-data sengaja kita biarkan hidup — tabel produk dan peserta di dalamnya menjadi titik berangkat chapter berikutnya. Dan justru di titik inilah satu disiplin terakhir perlu dipasang: volume melindungi data dari container, tetapi tidak dari apa pun yang terjadi pada satu disk itu sendiri. docker volume rm yang barusan kita jalankan memusnahkan data MySQL dan MariaDB tanpa sidang pengadilan — sebelum menjalankan hal serupa pada volume yang berisi data berharga, satu-satunya jalan aman adalah mencadangkannya lebih dulu, dan ritual pg_dump serta mysqldump untuk itu adalah materi Chapter Backup Database di seberang sana.
Sejauh ini inisialisasi kita selalu manual: container menyala, kita menyapa lewat klien, lalu mengetik CREATE TABLE dengan tangan sendiri. Rutinitas itu tidak akan bertahan di dunia nyata — rekan tim yang menjalankan compose.yaml di mesin baru tidak mau (dan tidak seharusnya) menyalin-tempel skema SQL dari obrolan. Docker menyediakan pintu resmi untuk hal ini: direktori docker-entrypoint-initdb.d yang isinya dijalankan otomatis pada inisialisasi pertama, ditambah pola service one-shot untuk mengisi data pada stack Compose. Chapter Inisialisasi Database menutup tarian inisialisasi yang selama ini kita kerjakan dengan tangan.
Referensi #
- Volumes — mekanisme volume bernama, mountpoint, dan pemisahan umur dari container.
- docker volume — CLI Reference
— perintah
inspect,ls, danrmbeserta aturan volume terpasang. - docker system df — CLI Reference
— ringkasan pemakaian disk dan flag
-vper objek. - docker compose down — CLI Reference
— perilaku
downterhadap volume dan makna flag-v.