Kita baru saja menutup Section Jaringan Container dengan perangkat yang lengkap: mulai dari network namespace dan bridge, DNS internal, sampai resep diagnosis ketika koneksi antar container macet. Dengan bekal itu, tidak ada stack multi-container yang tidak bisa kita rakit dengan tangan — buat network, nyalakan database di dalamnya, terbitkan port untuk aplikasi, lalu hubungkan semuanya. Justru di titik inilah masalahnya bergeser: yang melelahkan bukan lagi bisa atau tidaknya merakit stack, melainkan jumlah perintah yang harus diulang setiap kali stack itu hendak dinyalakan di mesin baru. Section ini memperkenalkan Docker Compose, alat yang mengubah seluruh deretan perintah manual tersebut menjadi satu berkas YAML yang bisa di-commit ke repositori, dibaca oleh rekan tim, dan dijalankan ulang dengan satu perintah tunggal.
Deretan Perintah yang Panjang dan Rapuh #
Budi memulai proyek baru bernama katalog — aplikasi web sederhana yang menyimpan daftar produk di PostgreSQL. Seluruh kebutuhannya sudah bisa dipenuhi dengan perintah-perintah yang kita kuasai dari section sebelumnya. Setiap kali Budi ingin menyalakan stack dari nol, inilah yang harus diketiknya:
$ docker network create net-katalog
$ docker run -d --name db-katalog \
--network net-katalog \
-e POSTGRES_USER=katalog \
-e POSTGRES_PASSWORD=rahasia123 \
-e POSTGRES_DB=katalog \
postgres:18-alpine
$ docker run -d --name web-katalog \
--network net-katalog \
-p 8080:80 \
nginx:1.29-alpineDua belas baris berisi flag, nama, dan nilai environment itu berhasil, dan setiap perintahnya meninggalkan jejak panjang di terminal.
Output yang Dihasilkan:
9e4c7b2a1f8d3c6a9b4e2f7d1c5a8e3b6f9d2c7a4e1b8f3d5c6a2e9b7d4f1a3c
c7a2e9b4f1d8c3a6e5b2f7d9c4a1e8b3f6d2a7c9e4b1f5d8a3c6e2b7f4a1d9c5e
3f8b1d6a9c2e7f4b3d1a8c5e2f7b4d9a6c1e3f8b2d7a4c9e6f1b3d5a8c2e7f4bBaris pertama adalah ID network yang baru tercipta, dua baris berikutnya ID kedua container — deretan panjang yang tidak kita baca lagi setelahnya. Perintah-perintah itu memang berhasil, tetapi berhasil dengan syarat yang banyak. Mari kita bedah empat titik nyerinya satu per satu:
- Panjang. Dua belas baris berisi flag, nama, dan nilai environment sebelum stack menyala. Satu salah ketik pada
POSTGRES_PASSWORDmembuat database lahir dengan kredensial yang berbeda dari yang aplikasi harapkan, dan galatnya baru terasa beberapa menit kemudian saat koneksi ditolak. - Urutan. Network harus lahir lebih dulu sebelum dua
docker runbisa bergabung ke dalamnya; database sebaiknya menyala sebelum aplikasi mencoba terhubung. Menukar urutan tidak selalu memicu galat langsung — kadang hanya menghasilkan kegagalan koneksi yang baru terasa dari dalam aplikasi, persis jenis masalah yang kita latih diagnosisnya pada Chapter Troubleshooting Network. - Dokumentasi. Setelah stack menyala, konfigurasi lengkapnya hanya hidup di riwayat shell dan ingatan Budi. Tidak ada satu berkas di proyek yang bisa dibaca ulang besok pagi untuk menjawab “port berapa saja yang diterbitkan? variabel apa saja yang dipasang pada db?”.
- Rekan tim. Siti ingin menjalankan katalog yang sama di mesinnya — dan satu-satunya jalan adalah menyalin-tempel deretan perintah itu dari obrolan, lalu berdoa flag-flagnya tidak terpotong. Padahal kisah Budi dan Siti pada Chapter Use Case Docker sudah menjanjikan masa depan yang lebih baik: satu berkas di repositori, satu perintah di mesin siapa pun.
Compose: Deklarasi dalam Satu Berkas YAML #
Docker Compose menjawab keempat nyeri itu dengan satu perubahan cara pandang: alih-alih mengeksekusi langkah demi langkah, kita mendeklarasikan kondisi akhir yang diinginkan di dalam satu berkas YAML, lalu meminta Compose mewujudkannya. Perintahnya ditulis docker compose — dua kata dengan spasi — karena Compose hadir sebagai plugin CLI yang menyatu pada perintah docker; kita sudah melihatnya tercantum sebagai compose* pada daftar Management Commands di Chapter docker help dan docker version, dan terpasang lewat paket plugin pada Chapter Instalasi Docker di Linux.
Perbedaan cara kerjanya terbaca paling jelas sebagai alur. Pendekatan manual mengeksekusi perintah satu per satu dan berhenti di situ; pendekatan deklaratif membaca berkas, membandingkannya dengan keadaan daemon, lalu mewujudkan selisihnya:
Mari kita telaah diagram di atas: cabang kiri adalah perilaku yang akan kita saksikan pada up berulang — container yang sudah cocok dengan deklarasi tidak disentuh; cabang kanan adalah yang terjadi ketika berkas berubah — Compose menciptakan ulang container yang berbeda. Kedua cabang berujung pada kondisi yang sama, dan itulah janji deklaratif: berkas selalu menang.
Berkas deklarasi itu punya nama resmi. Compose mencari empat nama berikut secara berurutan, lalu memakai yang pertama ditemukan:
| Prioritas | Nama berkas | Status |
|---|---|---|
| 1 | compose.yaml | nama resmi — pilihan utama untuk berkas baru |
| 2 | compose.yml | alternatif sah bila konvensi tim memakai .yml |
| 3 | docker-compose.yaml | nama warisan era Compose v1 — masih dikenali |
| 4 | docker-compose.yml | nama warisan era Compose v1 — masih dikenali |
Dua nama warisan pada prioritas tiga dan empat memakai tanda hubung di antara kata docker dan compose — bentuk yang diwariskan dari generasi pertama Compose. Proyek lama yang kita temui di lapangan sering masih memakai nama tersebut, tetapi seluruh Course ini menulis compose.yaml tanpa perkecualian, mengikuti konvensi resmi modern.
Sejarah singkatnya pantas dikenali karena menjelaskan mengapa dua kebiasaan lama masih berkeliaran di dokumentasi internet:
| Periode | Peristiwa |
|---|---|
| 2013 | Orchard memperkenalkan fig, tool orchestrator multi-container berbasis YAML yang pertama populer |
| 2014 | Docker, Inc. mengakuisisi Orchard; fig dibesarkan menjadi Docker Compose |
| 2014–2021 | Compose v1 — program Python terpisah dengan nama perintahnya sendiri |
| 2021 | Compose v2 — ditulis ulang dalam Go dan lahir sebagai plugin docker compose |
| 2026 | Versi yang terpasang di Course ini |
Versi yang berjalan di mesin kita bisa diperiksa dengan satu perintah:
$ docker compose versionOutput yang Dihasilkan:
Docker Compose version v5.6.0Angka v5.6.0 itu identik dengan yang tercantum pada daftar Plugins di output docker version — satu program yang sama, dipanggil dari dua pintu. Sepanjang section ini kita memakai bentuk plugin docker compose secara konsisten; bentuk lama dengan tanda hubung sudah berstatus legacy dan tidak lagi menerima fitur baru.
compose.yaml Pertama untuk Proyek katalog #
Saatnya mewujudkan janji Chapter Use Case Docker. Di dalam folder proyek katalog, Budi membuat berkas compose.yaml yang mendeklarasikan kedua service — web dan db — dalam bentuk paling minimal yang bisa jalan:
services:
web:
image: nginx:1.29-alpine
ports:
- "8080:80"
db:
image: postgres:18-alpine
environment:
- POSTGRES_PASSWORD=rahasia123Mari kita telaah isi berkas ini baris demi baris:
services:— satu-satunya blok yang wajib ada; seluruh container yang kita inginkan dideklarasikan di bawahnya sebagai pemetaan nama-ke-konfigurasi. Namawebdandbbebas kita pilih dan akan menjadi identitas resmi kedua service di sepanjang perintah Compose.image: nginx:1.29-alpine— service web memakai image siap pakai persis yang kita kenal dari Section Docker Image; untuk sekarang nginx berperan sebagai pengganti sementara aplikasi katalog yang sesungguhnya, dan akan kita ganti dengan image hasil build sendiri pada Chapter Image, Build, dan Pull.ports:dengan"8080:80"— padanan langsung flag-p 8080:80: port host 8080 diterbitkan menuju port 80 di dalam container, lengkap dengan tanda kutip yang maknanya kita bedah pada Chapter Struktur compose.yaml.environment:dengan- POSTGRES_PASSWORD=rahasia123— padanan flag-edari Chapter Environment Variable, ditulis sebagai daftar; tanpa variabel ini container postgres menolak menyala, persis peringatan yang sudah kita saksikan pada Chapter Use Case Docker.- Tidak ada
version:dan tidak adanetworks:— kunciversion:sudah obsolete dan sengaja tidak ditulis; blok network juga tidak kita deklarasikan karena Compose otomatis membuat satu network bersama untuk seluruh service dalam berkas, pengetahuan yang dasarnya kita pelajari pada Section Jaringan Container dan yang akan kita bedah tuntas pada Chapter Port dan Network.
Sepuluh baris YAML di atas menggantikan seluruh deretan docker network create dan dua docker run — dan ia tersimpan sebagai berkas biasa yang bisa di-commit.
Satu Perintah untuk Menyalakan Seluruh Stack #
Budi meng-commit compose.yaml ke repositori katalog dan mendorongnya. Di mesinnya sendiri ia tinggal menjalankan satu perintah dari dalam folder proyek; output berikut diambil dari laptop Siti yang baru menarik repositori yang sama — laptopnya belum pernah menarik kedua image, sehingga proses penarikannya ikut terlihat:
$ docker compose upOutput yang Dihasilkan:
[+] Pulling 2/2
✔ db Pulled 7.2s
✔ web Pulled 4.9s
[+] Running 3/3
✔ Network katalog_default Created 0.2s
✔ Container katalog-db-1 Created 0.1s
✔ Container katalog-web-1 Created 0.0s
✔ Container katalog-db-1 Started 0.4s
✔ Container katalog-web-1 Started 0.3s
Attaching to katalog-db-1, katalog-web-1
katalog-db-1 | The files belonging to this database system will be owned by user "postgres".
katalog-db-1 | This user must also own the server process.
katalog-db-1 |
katalog-db-1 | initdb: warning: enabling "trust" authentication for local connections only.
...
katalog-db-1 | 2026-10-13 16:12:08.447 UTC [1] LOG: starting PostgreSQL 18.4 on x86_64-pc-linux-musl, compiled by gcc (Alpine 14.2.0) 14.2.0, 64-bit
katalog-db-1 | 2026-10-13 16:12:08.612 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432
katalog-db-1 | 2026-10-13 16:12:08.731 UTC [1] LOG: database system is ready to accept connections
katalog-web-1 | /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to process configuration files
katalog-web-1 | /docker-entrypoint.sh: Configuration complete; ready for start up
katalog-web-1 | 2026/10/13 16:12:09 [notice] 1#1: using the "epoll" event method
katalog-web-1 | 2026/10/13 16:12:09 [notice] 1#1: start worker processes
katalog-web-1 | 2026/10/13 16:12:09 [notice] 1#1: start worker process 39
...Baris-baris inisialisasi initdb di bagian tengah dipotong agar ringkas. Mari kita telaah apa yang Compose kerjakan dari output di atas:
[+] Pulling 2/2— Compose memeriksa kedua image yang dideklarasikan; di laptop Siti keduanya belum ada, sehingga Compose menariknya lebih dulu persis sepertidocker pull, tanpa satu perintah tambahan dari pengguna.✔ Network katalog_default Created— network dibuat otomatis dan dinamai mengikuti nama project; nama project sendiri diambil dari nama folder, yaitukatalog, sehingga network-nya bernamakatalog_default. Inilah padanandocker network create net-katalogyang dulu Budi ketik manual.katalog-db-1dankatalog-web-1— container dinamai dengan pola<project>-<service>-<index>; tidak ada satu pun flag--nameyang kita tulis, dan pola penamaan ini akan kita bedah perannya pada Chapter Service.Attaching to ...dan log gabungan — karenaupdijalankan tanpa-d, Compose menempelkan terminal ke log kedua container sekaligus; setiap baris diawali nama container pengirimnya, sehingga riwayat inisialisasi postgres dan startup nginx terbaca berdampingan dalam satu tampilan.- Tekan Ctrl+C untuk melepas tampilan log dan menghentikan kedua container — Compose mengirim sinyal berhenti kepada keduanya secara tertib, dan nama stack katalog tetap tercatat sehingga bisa dinyalakan lagi kapan pun.
Pembongkarannya sama singkatnya. Dari terminal yang sama, Siti menjalankan:
$ docker compose downOutput yang Dihasilkan:
[+] Running 3/3
✔ Container katalog-web-1 Removed 0.4s
✔ Container katalog-db-1 Removed 0.5s
✔ Network katalog_default Removed 0.2sMari kita telaah hasil down tersebut: kedua container dihapus dari daemon, lalu network bawaan project ikut dibongkar — tiga objek yang diciptakan up dibersihkan utuh oleh satu perintah, dan menjalankan up kembali besok pagi akan menciptakan ketiganya dari nol dengan konfigurasi yang sama persis karena sumber kebenarannya adalah berkas, bukan ingatan.
Kilas ke depan sebelum kita melangkah: sepanjang section ini, deretan tanda ✔ pada output up akan semakin kaya. Begitu service db memasang healthcheck dan web menyatakan dirinya menunggu, output stack katalog akan tumbuh menjadi seperti ini:
[+] Running 3/3
✔ Network katalog_default Created
✔ Container katalog-db-1 Healthy
✔ Container katalog-web-1 StartedBaris ✔ Container katalog-db-1 Healthy adalah gerbang kesehatan yang kita bangun pada Chapter Healthcheck dan Service Readiness — persis perilaku “menunggu database sehat, baru menyalakan aplikasi” yang diceritakan Chapter Use Case Docker. Seluruh fitur yang menanti di depan bisa dipetakan begitu saja:
| Fitur | Kunci di compose.yaml | Dibahas di |
|---|---|---|
| Service dan container | services | Service |
| Image siap pakai dan build sendiri | image, build | Image, Build, dan Pull |
| Port dan network antar service | ports, expose, networks | Port dan Network |
| Persistensi data | volumes | Volume dan Bind Mount |
| Environment variable | environment, env_file | Environment dan env File |
| Urutan start antar service | depends_on | depends_on dan Startup Order |
| Kesehatan service | healthcheck | Healthcheck dan Service Readiness |
| Service opsional per skenario | profiles | Profiles |
| Sinkronisasi kode saat develop | develop.watch | Compose Watch |
Berkas sepuluh baris tadi adalah pintu masuknya, dan setiap baris pada tabel di atas akan menambah satu kemampuan pada berkas yang sama. Sebelum menumbuhkannya, kita perlu membaca peta anatomi berkas itu dengan saksama: blok apa saja yang sah, bagaimana aturan indentasinya, dan bagaimana memvalidasinya tanpa menjalankan satu container pun. Chapter Struktur compose.yaml membedah peta tersebut.
Referensi #
- Docker Compose — halaman resmi Compose: definisi, filosofi deklaratif, dan daftar kemampuannya.
- Compose file reference — struktur berkas compose.yaml beserta seluruh blok top-level yang sah.
- The Compose Specification — spesifikasi terbuka yang menjadi acuan implementasi Compose modern.