Berkas sepuluh baris pada chapter sebelumnya sudah terbukti bekerja: dua service menyala, satu network tercipta, dan seluruh output berjalan sesuai janji. Tetapi berkas yang kita tulis itu baru memakai satu blok — services — sementara compose.yaml yang utuh mengenal tujuh blok top-level dengan aturan mainnya masing-masing. Chapter ini membedah anatomi lengkap berkas tersebut: prioritas nama berkas, peta blok top-level, identitas project lewat name, penghematan pengulangan dengan x- fragment, aturan indentasi, hingga kunci version: yang sudah pensiun. Di ujungnya kita akan menguji semua pengetahuan itu dengan satu perintah pemeriksa yang merender berkas final tanpa menjalankan satu container pun.
Empat Nama Berkas dan Prioritasnya #
Compose mencari berkas definisi di folder tempat perintah dijalankan, dengan urutan pencarian yang pasti. Ketika dijalankan tanpa flag -f, Compose memeriksa empat nama berikut dan memakai yang pertama ditemukan:
| Prioritas | Nama berkas | Status |
|---|---|---|
| 1 | compose.yaml | nama resmi — selalu pilih ini 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 |
Kedua nama warisan memakai tanda hubung di antara kata docker dan compose, mewarisi nama perintah generasi pertama Compose; banyak proyek lama di lapangan masih menyimpannya. Kebiasaan kita sepanjang Course ini cukup satu: menulis compose.yaml.
Peta Blok Top-Level #
Tujuh blok sah boleh hadir di level teratas sebuah compose.yaml. Peta berikut merangkum semuanya sebelum kita memasuki satu per satu:
| Blok | Wajib | Peran |
|---|---|---|
services | ya — satu-satunya blok wajib | cetakbiru container yang akan dijalankan |
name | tidak | identitas project: prefix nama container, network, dan volume |
networks | tidak | network yang dirujuk service, di luar bawaan project |
volumes | tidak | volume bernama yang dirujuk service |
configs | tidak | berkas konfigurasi yang disuntikkan ke service |
secrets | tidak | berkas rahasia yang disuntikkan ke service |
x-<apa pun> | tidak | gudang nilai untuk dipakai ulang — diawali x- |
Susunan lengkapnya terbaca paling jelas lewat satu kerangka. Peta berikut meminjam isi dari chapter-chapter mendatang supaya seluruh blok tampil sekaligus dalam satu potret:
name: katalog
x-umum: &umum
restart: unless-stopped
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
services:
web:
<<: *umum
image: nginx:1.29-alpine
ports:
- "8080:80"
networks:
- frontend
db:
<<: *umum
image: postgres:18-alpine
environment:
- POSTGRES_PASSWORD=rahasia123
volumes:
- db-data:/var/lib/postgresql/data
networks:
- backend
networks:
frontend:
driver: bridge
backend:
driver: bridge
volumes:
db-data:
configs:
nginx-conf:
file: ./nginx.conf
secrets:
kunci-api:
file: ./kunci-api.txtMari kita telaah struktur kerangka tersebut:
name: katalog— identitas project yang akan menjadi prefix seluruh objek kelolaannya; perannya kita bedah tuntas di bagian berikutnya.x-umumdengan&umumdan<<: *umum— pasangan penanda (anchor) dan rujukan (alias) milik YAML untuk menyalin konfigurasi berulang tanpa duplikasi; mekanismenya mendapat bagian khusus setelah ini.services— jantung berkas; setiap anak dari blok ini adalah satu service, dan hanya blok ini yang wajib ada.networksdanvolumestop-level — mendaftarkan objek yang dirujuk dari dalam service:frontenddanbackendakan lahir sebagaikatalog_frontenddankatalog_backend, sedangkandb-dataadalah volume bernama untuk data PostgreSQL. Pembahasan lengkapnya menanti Chapter Port dan Network dan Volume dan Bind Mount.configsdansecrets— dua blok untuk menyuntikkan berkas ke dalam container; keduanya sengaja diletakkan paling bawah karena membutuhkan berkas sumber di build context.
name: Identitas Project #
Tanpa blok name, Compose menamai project mengikuti nama folder tempat berkas berada — folder katalog melahirkan project katalog, dan itulah yang kita saksikan pada chapter sebelumnya ketika network tercipta bernama katalog_default. Menuliskan name: katalog secara eksplisit melepaskan project dari kebetulan nama folder: nama project menjadi bagian dari deklarasi, bukan warisan struktur direktori.
Nama project itu menempel pada tiga jenis objek sekaligus. Container menjalankan pola <project>-<service>-<index> seperti katalog-db-1, network bawaan bernama <project>_default seperti katalog_default, dan volume bernama kelak mengikuti <project>_<volume>. Identitas yang sama juga menjadi kunci perintah-perintah manajemen: docker compose ps dan docker compose down mencari container berdasarkan label project ini.
Dua jalur lain bisa memaksa nama project menimpa nilai yang tertulis di berkas: flag -p pada perintah, misalnya docker compose -p katalog-uji up, dan environment variable COMPOSE_PROJECT_NAME di shell. Kedua jalur itu berguna saat satu berkas yang sama perlu berjalan sebagai dua project berdampingan — skenario yang kita peragakan pada Chapter Service.
x- Fragments: Menyalin Tanpa Menulis Ulang #
Konfigurasi yang sama sering dibutuhkan lebih dari satu service, dan YAML menyediakan dua mekanisme salin-menyalin untuk itu. Yang pertama adalah penanda dan rujukan: nilai yang ditandai &nama boleh dirujuk di tempat lain dengan *nama. Compose menambahkan konvensi x- untuk blok gudang — blok yang diawali x- diabaikan Compose sebagai konfigurasi, sehingga aman dipakai sebagai tempat menaruh nilai:
x-logging-umum: &logging-umum
driver: json-file
options:
max-size: "10m"
max-file: "3"
services:
web:
image: nginx:1.29-alpine
ports:
- "8080:80"
logging: *logging-umum
db:
image: postgres:18-alpine
environment:
- POSTGRES_PASSWORD=rahasia123
logging: *logging-umumMari kita telaah mekanisme salinnya: blok x-logging-umum menyimpan peta konfigurasi logging lengkap dengan penanda &logging-umum, lalu kedua service merujuk peta yang sama dengan logging: *logging-umum. Perubahan satu nilai max-size di satu tempat langsung berlaku untuk dua service — penghematan yang persis semangat DRY pada kode program.
Mekanisme kedua adalah merge key <<: yang kita lihat pada kerangka peta di atas. Alih-alih menyalin satu kunci, merge key menyuntikkan seluruh pasangan kunci-nilai dari rujukan ke dalam pemetaan yang menerima:
x-umum: &umum
restart: unless-stopped
logging:
driver: json-file
services:
web:
<<: *umum
image: nginx:1.29-alpine
db:
<<: *umum
image: postgres:18-alpineKedua service menerima restart dan logging sekaligus tanpa menulisnya, sementara kunci miliknya sendiri seperti image tetap ditulis biasa. Kunci yang ditulis ulang di bawah <<: akan menimpa nilai hasil merge, sehingga pengecualian tetap mungkin. Peringatannya soal keterbacaan: pembaca berkas harus melompat ke definisi penanda untuk mengetahui konfigurasi final sebuah service, dan tumpukan merge bertingkat menjadikan berkas cepat sulit dibaca manusia. Gunakan kedua mekanisme ini untuk blok yang benar-benar seragam, dan biarkan perbedaan antar service tertulis eksplisit — docker compose config di bawah selalu siap menunjukkan hasil akhir penggabungannya.
Indentasi Dua Spasi dan Kunci version: yang Pensiun #
YAML memakai indentasi sebagai struktur: kedalaman baris menentukan kepemilikan kunci. Compose mengikuti kebiasaan ekosistem Docker dengan indentasi dua spasi per tingkat, seperti seluruh contoh pada chapter ini. Aturan ketatnya satu: karakter tab dilarang untuk indentasi — parser YAML menolaknya, dan kita akan melihat wujud galatnya pada bagian terakhir chapter.
Satu kunci lama perlu dimakamkan dengan sadar: version:. Berkas-berkas warisan era awal kerap diawali baris yang menyatakan versi skema — nilai seperti "3.8" — dan pada generasi tersebut ketidakcocokan angka dengan fitur yang dipakai bisa menggugurkan perintah dengan galat validasi. Compose modern menjadikan kunci ini obsolete: skema yang berlaku sudah tunggal, sehingga baris tersebut tidak lagi berarti apa pun. Inilah perubahan yang dianjurkan pada berkas warisan yang kita temui:
-version: "3.8"
services:
web:
image: nginx:1.29-alpine
ports:
- "8080:80"
Bila baris itu dibiarkan, Compose masih mau memproses berkas, tetapi setiap perintah diawali peringatan yang menyebut jalurnya persis:
WARN[0000] /home/budi/katalog/compose.yaml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusionMari kita telaah tiga keterangan dari peringatan itu: pertama, statusnya peringatan bukan galat — berkas tetap diproses dan kunci diabaikan; kedua, Compose menyebut berkas dan atribut yang jadi sumbernya sehingga sumber kebisingan mudah ditemukan; ketiga, sarannya tegas — hapus barisnya, seperti sisi kanan diff di atas. Sepanjang Course ini kita tidak pernah menulis kunci tersebut.
YAML atau JSON #
JSON sah dipakai sebagai isi berkas karena YAML 1.2 merangkum JSON sebagai subset — Compose akan menerimanya. Kedua format itu tetap tidak setara dalam kenyamanan sehari-hari:
| Aspek | YAML | JSON |
|---|---|---|
| Keterbacaan manusia | struktur tampak dari indentasi dua spasi | padat tanda kurung kurawal dan tanda kutip |
| Komentar | didukung dengan awalan # | tidak didukung sama sekali |
| Penghematan pengulangan | penanda, alias, dan merge key | tidak tersedia |
| Kedudukan di Compose | format asli seluruh contoh resmi | sah sebagai subset — nyaris tak dipakai komunitas |
Kehilangan komentar saja sudah cukup sebagai alasan: berkas deklarasi adalah dokumen yang dibaca manusia, dan kemampuan mencatat alasan di samping nilai konfigurasi termasuk yang paling berharga.
docker compose config: Merender Berkas Final #
Sejauh ini kita memeriksa berkas dengan menjalankannya — cara yang mahal. Compose menyediakan pemeriksa yang jauh lebih murah: docker compose config membaca berkas, menyelesaikan seluruh mekanisme penggabungan, lalu mencetak konfigurasi final yang akan dipakai daemon. Mari kita jalankan pada berkas katalog yang kini sudah memakai name::
name: katalog
services:
web:
image: nginx:1.29-alpine
ports:
- "8080:80"
db:
image: postgres:18-alpine
environment:
- POSTGRES_PASSWORD=rahasia123$ docker compose configOutput yang Dihasilkan:
name: katalog
services:
db:
environment:
POSTGRES_PASSWORD: rahasia123
image: postgres:18-alpine
networks:
default: null
web:
image: nginx:1.29-alpine
networks:
default: null
ports:
- target: 80
published: "8080"
protocol: tcp
mode: ingress
networks:
default:
name: katalog_defaultMari kita telaah enam perubahan yang dilakukan renderer:
name: katalogtercetak pertama — identitas project dari bloknameikut menjadi bagian konfigurasi final; bila blok itu tidak ditulis, renderer akan mengisinya dengan nama folder sebagai sumber cadangan.- Urutan kunci disusun ulang — service
dbtampil mendahuluiwebmeskipun berkas menulisweblebih dulu; hasil render adalah konfigurasi yang sudah dinormalisasi Compose, bukan salinan teks berkas. environmentberubah bentuk — daftar- POSTGRES_PASSWORD=rahasia123dirender sebagai pemetaanPOSTGRES_PASSWORD: rahasia123; kedua bentuk sah untuk ditulis, dan renderer menyeragamkannya ke bentuk pemetaan.- Network implisit menjadi eksplisit — setiap service kini mencantumkan
networks: default: null, dan bloknetworkstop-level lahir dengandefault: name: katalog_default; inilah bukti render bahwa network bawaan project memang diberikan Compose tanpa kita deklarasikan. portsdiperluas menjadi long syntax — string pendek"8080:80"dibuka menjadi empat kunci:target80 di container,published“8080” di host,protocoltcp, danmodeingress; bentuk panjang ini bisa pula kita tulis sendiri, materi Chapter Port dan Network.publisheddirender dengan tanda kutip — renderer sengaja menjaga nomor port sebagai string"8080"; alasan di balik tanda kutip itu adalah kejelian YAML terhadap polaxx:yyyang akan kita bedah pada chapter yang sama.
Untuk skrip dan pipeline CI, flag --quiet membuat perintah ini memvalidasi berkas tanpa mencetak apa pun: keberhasilan dilaporkan lewat exit code 0, sehingga docker compose config --quiet layak menjadi gerbang pemeriksaan sebelum deployment di skrip otomatis.
Dua Kesalahan yang Paling Sering Terjadi #
Dua kesalahan tulis mengalahkan pemula lebih sering daripada materi lainnya, dan keduanya sama-sama mudah dikenali dari pesannya. Yang pertama: menulis services sebagai daftar, terbawa kebiasaan menulis elemen bertanda hubung:
services:
- web:
image: nginx:1.29-alpine
- db:
image: postgres:18-alpinevalidating /home/budi/katalog/compose.yaml: services must be a mappingPerbaikannya adalah menghapus tanda hubung dan menjadikan kedua service anak langsung dari services: — pemetaan nama-ke-konfigurasi, bukan daftar. YAML menerima kedua bentuk itu sebagai dokumen yang sah, sehingga tanggung jawab menolak jatuh ke validasi Compose; pesan services must be a mapping menyebut blok yang salah bentuk dengan tepat.
Yang kedua: indentasi dengan karakter tab, biasanya disisipkan diam-diam oleh editor:
services:
web:
image: nginx:1.29-alpineyaml: line 2: found character that cannot start any tokenPerbaikannya adalah mengganti setiap tab dengan dua spasi pada baris yang disebut — dan pada seluruh berkas, karena tab berikutnya akan memicu galat yang sama di barisnya masing-masing. Perhatikan bahwa pesan ini datang dari parser YAML itu sendiri sebelum Compose sempat memvalidasi apa pun: nomor barisnya adalah petunjuk lokasi pertama karakter tab ditemukan.
Anatomi berkas kini terpetakan: nama berkas dan prioritasnya, tujuh blok top-level, identitas project, mekanisme salin x-, indentasi dua spasi, hingga cara merender dan memvalidasinya. Namun dari seluruh blok itu, hanya satu yang benar-benar menjalankan container: services. Chapter Service membedah blok tersebut — hubungan service dengan container yang ia instansiasi, pola penamaan <project>-<service>-<index>, dan delapan belas kunci konfigurasi yang paling sering dipakai sehari-hari.
Referensi #
- Compose file reference — peta lengkap seluruh blok top-level beserta kunci dan nilai bawaannya.
- The Compose Specification
— spesifikasi terbuka yang mendefinisikan tiap blok, termasuk
namedan extension fields. - docker compose CLI reference
— daftar perintah Compose, termasuk
configdan flag--quiet.