# Belajar YAML untuk Pemula: Map, List, Quote, dan Jebakannya

> Belajar YAML untuk pemula: map, list, indentasi, string multiline, dan tanda kutip, plus jebakan yang mengubah on, NO, dan 3.10 menjadi nilai lain.

URL: https://www.matthewswong.com/id/learn/devops-from-zero/yaml-tutorial-for-beginners/

DevOps dari nol · Pelajaran 21 dari 25 · Ditinjau pada 18 Oktober 2026 · Oleh Matthews Wong (https://www.matthewswong.com/id/about/)

> YAML adalah format teks biasa untuk data terstruktur: indentasi dengan spasi menyusun map (key: value) dan list (- item) secara bersarang, dan nilai lainnya adalah scalar. Docker Compose dan GitHub Actions sama-sama membacanya. Parser, bukan Anda, yang menentukan tipe setiap nilai tanpa tanda kutip, jadi on, NO, 0755, dan 3.10 bisa berubah menjadi boolean atau angka lain kalau tidak diberi tanda kutip.

## Setelah pelajaran ini, Anda bisa

- Membaca file YAML apa pun sebagai pohon berisi map, list, dan scalar, lalu menunjukkan setiap baris yang menjorok itu milik siapa.
- Menulis string multiline dengan | dan >, dan tahu nilai mana yang butuh tanda kutip di YAML 1.1 dan 1.2.
- Mem-parse file ke JSON di laptop untuk melihat persis apa yang akan dibaca sebuah tool, dan membedakan YAML dari ${VAR} milik Compose serta ${{ }} milik GitHub.

## Bagaimana parser mengubah YAML menjadi pohon

File Compose melewati parser YAML sebelum Compose melihatnya: indentasi menentukan apa yang bersarang di bawah apa, tab menghentikan parser, nilai tanpa tanda kutip diberi tipe sesuai bentuknya, dan Compose baru mengisi variabelnya sendiri setelah parsing.

1. **Kenali bagiannya.** File YAML hanyalah teks. Parser membacanya lalu membangun pohon berisi map, list, dan scalar, dan Docker Compose bekerja dengan pohon itu, bukan dengan teksnya.
2. **Indentasi membentuk susunan.** Key yang menjorok lebih dalam dari key di atasnya adalah anaknya: web di bawah services, ports di bawah web. Tanda strip memulai item list, jadi ports berisi sebuah list.

   ```
   services:
     web:
       ports:
   ```

   *Tebak:* Berikutnya, ports: digeser dua spasi ke kiri, sejajar dengan web:. Di mana parser menaruhnya? (Tetap di dalam web / Di samping web, sebagai service kedua)
3. **Salah geser dua spasi.** Sejajar dengan web, ports menjadi saudaranya: service kedua bernama ports. YAML-nya valid, jadi parser tidak protes. Compose yang protes: services.ports must be a mapping.

   ```
   services:
     web:
     ports:
   ```
4. **Tab menghentikan parser.** YAML melarang tab untuk indentasi. Satu tab di depan sebuah key membuat parser berhenti di baris itu, jadi Compose tidak pernah menerima pohon: found character that cannot start any token.

   ```
   found character that cannot start any token
   ```

   *Tebak:* Berikutnya, item list ditulis 22:22 tanpa tanda kutip dan dibaca parser YAML 1.1. Apa yang masuk ke pohon? (Teks 22:22 / Angka 1342 / Error saat parsing)
5. **Parser memilih tipenya.** Nilai tanpa tanda kutip diberi tipe sesuai bentuknya. Bagi parser YAML 1.1, 22:22 adalah bilangan basis 60: angka 1342. Referensi Docker meminta port mapping selalu diberi tanda kutip.

   ```
   - 22:22
   ```
6. **Tanda kutip menjadikannya teks.** Dengan tanda kutip, "22:22" menjadi string di semua versi YAML, dan Compose menerima persis yang Anda tulis. Entri port situs ini juga diberi tanda kutip, sesuai anjuran referensi Docker.

   ```
   - "22:22"
   ```
7. **Sintaks milik tool.** Di dalam string, variabel berawalan tanda dolar tidak berarti apa-apa bagi YAML. Compose mengisinya setelah parsing, dari shell atau .env, dan GitHub Actions mengevaluasi expression-nya dengan cara yang sama.

   ```
   published: "3002"
   ```

## Apa itu YAML?

YAML adalah bahasa serialisasi data: cara menulis data terstruktur sebagai teks biasa yang bisa dibaca dan diedit manusia. Spesifikasinya menyebut YAML ramah manusia dan lintas bahasa pemrograman. Setiap dokumen YAML tersusun dari tiga jenis node: mapping (pasangan key: value), sequence (list), dan scalar (satu nilai, misalnya string atau angka).

Di dunia DevOps, YAML ada di mana-mana. Docker Compose mendeskripsikan aplikasi dalam file YAML, secara default bernama compose.yaml, dan masih menerima docker-compose.yml, nama lama yang dipakai situs ini. Workflow GitHub Actions memakai sintaks YAML dan harus berakhiran .yml atau .yaml. Spesifikasi versi 1.2 dirancang agar YAML menjadi superset ketat dari JSON, jadi dokumen JSON juga merupakan YAML yang valid.

Ada satu gagasan yang mengikat pelajaran ini: tool tidak pernah membaca teks Anda. Parser YAML membacanya lebih dulu, lalu menyerahkan pohon berisi map, list, dan nilai bertipe ke tool tersebut. Setiap jebakan di bawah adalah selisih antara pohon yang Anda maksud dan pohon yang dibangun parser.

## Bagaimana indentasi membentuk struktur?

Indentasi adalah strukturnya. Aturan di spesifikasi: setiap node harus menjorok lebih dalam dari induknya, dan semua node yang bersaudara harus memakai indentasi yang persis sama. Jumlah spasinya bebas, karena itu hanya detail tampilan; file di situs ini memakai dua spasi. Yang penting, node yang bersaudara harus sejajar.

Pakai spasi saja untuk indentasi. Spesifikasi melarang karakter tab di indentasi, karena tiap sistem memperlakukan tab secara berbeda. Dalam uji coba pada tanggal riset, satu tab di depan sebuah key menghentikan ketiga parser yang dicoba: PyYAML, js-yaml, dan Docker Compose, yang melaporkan found character that cannot start any token.

Kesalahan yang berbahaya justru yang tetap lolos parsing. Geser ports dua spasi ke kiri, sejajar dengan web, maka ia bukan lagi pengaturan milik web: ia menjadi entri kedua di bawah services, yaitu service bernama ports. YAML-nya valid, jadi hanya tool yang bisa protes, dan Compose memang protes: services.ports must be a mapping.

## Map, list, flow style, dan komentar

Entri mapping ditulis key: value, dengan titik dua lalu spasi. Entri sequence diawali tanda strip lalu spasi. Keduanya bisa bersarang dengan bebas: di workflow situs ini, steps adalah list, dan setiap item di dalamnya adalah map dengan key seperti name, uses, dan with.

Keduanya punya flow style yang diambil dari JSON: kurung siku untuk list, kurung kurawal untuk map. branches: [main] di workflow sama saja dengan list yang ditulis dengan tanda strip di barisnya sendiri, dan test pada health check di file compose adalah flow list yang dipecah ke beberapa baris. Di dalam kurung siku dan kurawal, nilai tanpa tanda kutip tidak boleh berisi kurung siku, kurung kurawal, atau koma.

Key tanpa nilai di belakangnya berisi null: parse workflow situs ini dan workflow_dispatch keluar sebagai null. Key dalam satu map harus unik. Saat diberi key ganda, js-yaml dan Compose berhenti dengan error, sedangkan PyYAML diam-diam memakai nilai terakhir.

Tanda # memulai komentar sampai akhir baris, dan komentar tidak berpengaruh apa pun pada pohon. Tanda itu harus dipisahkan dari nilai dengan spasi, jadi color: #fff bukan warna: parser melihat key dengan nilai kosong, yaitu null, lalu sebuah komentar. Tanda # tanpa spasi di depannya, seperti fragment pada URL, tetap menjadi bagian dari nilai.

## Bagaimana menulis string multiline dengan | dan >?

Block scalar menampung teks beberapa baris yang menjorok di bawah key-nya. Dengan | (literal), setiap pergantian baris dipertahankan. Dengan > (folded), pergantian baris tunggal menjadi spasi, jadi kalimat panjang boleh dipecah di file dan tetap menjadi satu baris. Indentasi di depan blok dibuang; parser hanya menyimpan teksnya.

Karakter setelah | atau > mengatur bagian akhirnya. | biasa menyisakan tepat satu newline di akhir, perilaku default yang di spesifikasi disebut clip. |- membuangnya, dan |+ mempertahankan semua baris kosong di akhir. Dalam uji coba, dua baris di bawah | kembali sebagai dua baris yang diakhiri newline, sedangkan baris yang sama di bawah > kembali tergabung oleh spasi.

Langkah deploy situs ini menulis script shell-nya di bawah script: |, karena setiap baris harus tetap menjadi perintah tersendiri. run: di GitHub bekerja dengan cara yang sama; referensinya menyebut bahwa saat Anda memberi perintah multiline, setiap baris dijalankan di shell yang sama.

## Kapan butuh tanda kutip di YAML?

Nilai tanpa tanda kutip disebut plain scalar, dan parser menentukan tipenya dengan mencocokkannya ke sebuah schema. Di core schema YAML 1.2, hanya true dan false (dalam tiga variasi huruf besar-kecil) yang menjadi boolean; null, Null, NULL, ~, dan nilai kosong menjadi null; integer berupa desimal, oktal 0o, atau heksadesimal 0x; dan angka dengan titik atau eksponen menjadi float. Apa pun yang tidak cocok dengan semua itu adalah string.

YAML 1.1 mencocokkan jauh lebih banyak. Tipe boolean-nya mencakup y, n, yes, no, on, dan off dalam beberapa variasi huruf; integer-nya menganggap angka berawalan 0 sebagai oktal dan menerima basis 60, jadi 190:20:30 adalah angka. Dari sinilah jebakan terkenal itu berasal: bagi parser YAML 1.1, country: NO bernilai false dan key on: bernilai true.

Ada satu jebakan yang bertahan di kedua versi. 3.10 cocok dengan aturan float di YAML 1.1 maupun 1.2, jadi berubah menjadi angka 3.1. Semua parser yang diuji mengubah 3.10 tanpa tanda kutip menjadi 3.1, karena itu nomor versi sebaiknya diberi tanda kutip; tutorial Python milik GitHub sendiri menulis matrix-nya sebagai string bertanda kutip seperti "3.9" dan "3.10".

Beri tanda kutip pada nilai yang sebenarnya teks tetapi mirip sesuatu yang lain: nomor versi, kode berawalan nol, kode negara, yes atau no, on atau off, bentuk seperti jam 22:22. Beri tanda kutip juga kalau nilainya diawali karakter yang dicadangkan YAML: * memulai alias, & anchor, ! tag, | dan > blok, # komentar, sedangkan @ dan backtick dicadangkan. Tanda kutip ganda mengizinkan escape dengan backslash; tanda kutip tunggal mengambil teks apa adanya, dengan dua apostrof untuk menulis satu apostrof.

## Parser mana yang membaca file Anda?

Apakah NO itu boolean bergantung pada parser-nya, dan tool yang Anda pakai tidak sepakat. PyYAML menyebut dirinya parser YAML 1.1 yang lengkap, jadi ia membaca on, NO, dan yes sebagai boolean. js-yaml menyebut dirinya parser YAML 1.2 dan membiarkan ketiganya sebagai teks. Docker Compose memuat file dengan package yaml untuk Go, yang mendukung sebagian besar YAML 1.2 tetapi mempertahankan sebagian perilaku YAML 1.1 demi kompatibilitas.

Tabel di bawah adalah hasil uji coba pada tanggal riset. Compose mempertahankan yes, NO, dan on sebagai teks, tetapi membaca 0755 sebagai angka oktal 493, sehingga entri environment yang ditulis MODE: 0755 dirender sebagai string 493. Referensi Compose milik Docker tetap meminta yes dan no di environment diberi tanda kutip, dan port mapping ditulis sebagai string bertanda kutip karena parsing basis 60. Mengikuti anjuran itu lebih mudah daripada memeriksa aturan mana yang dipakai versi Anda.

GitHub Actions membaca on: sebagai key pemicu, seperti terlihat di setiap contoh pada referensinya. Namun kalau deploy.yml situs ini dibaca dengan PyYAML, key level atas yang keluar adalah name, True, dan jobs, sehingga script apa pun yang mencari pemicu di bawah on akan gagal. Referensi GitHub juga mengingatkan bahwa ! adalah notasi yang dicadangkan di YAML, jadi kondisi yang diawali ! harus dibungkus ${{ }} atau diberi tanda kutip.

## Anchor, alias, dan merge key

Anchor, &nama, memberi label pada sebuah node; alias, *nama, mengulanginya di tempat lain dalam file yang sama. Compose mendukung keduanya, ditambah merge key <<, yang menyalin isi sebuah map ke map lain sehingga sebagian bisa Anda timpa. Simpan blok yang dipakai bersama di field x- level atas, yang diabaikan Compose. Referensinya menambahkan dua batasan: merge hanya berlaku untuk map, bukan list, dan anchor di-resolve sebelum interpolasi variabel, jadi variabel tidak bisa dipakai untuk menamai anchor.

GitHub Actions mendukung anchor dan alias sejak September 2025, misalnya untuk memakai ulang satu blok env di dua job. Dokumentasinya tidak menyebut merge key, jadi jangan mengandalkan << di workflow. Untuk sesuatu yang lebih besar dari sekadar blok yang berulang, GitHub punya reusable workflow.

## Apakah ${VAR} dan ${{ }} bagian dari YAML?

Bukan. YAML tidak punya variabel; bagi parser, ${APP_PORT:-3002} di file compose situs ini hanyalah teks biasa di dalam string. Compose menggantinya setelah itu lewat interpolasi bergaya Bash: ${VAR} atau $VAR, ${VAR:-default} kalau variabelnya tidak di-set atau kosong, ${VAR:?pesan} untuk berhenti dengan error. Nilainya diambil dari shell lebih dulu, lalu dari file yang diberikan lewat --env-file, kalau tidak ada dari file .env di samping file compose. Interpolasi hanya berlaku untuk nilai, tidak untuk key, dan $$ menulis tanda dolar apa adanya.

docker compose config menampilkan hasilnya: perintah ini mem-parse, me-resolve, dan merender file dalam bentuk kanonis, dengan --format json untuk keluaran JSON dan --no-interpolate untuk melihat teks mentahnya. Tanpa variabel apa pun, entri port situs ini dirender sebagai published 3002; dengan APP_PORT=8080 di shell, menjadi 8080.

${{ }} adalah sintaks expression milik GitHub. Dokumentasinya menyebut sintaks khusus ini memberi tahu GitHub untuk mengevaluasi expression alih-alih memperlakukannya sebagai string; bagi parser YAML, host: ${{ secrets.VPS_SSH_HOST }} hanyalah string yang diawali tanda dolar. Di script run, GitHub mengevaluasi expression lalu menyisipkan hasilnya ke teks script sebelum shell menjalankannya, karena itu input yang tidak tepercaya tidak boleh ditaruh di dalam ${{ }} pada script.

## Konfigurasi nyata di balik pelajaran ini

`docker-compose.yml:24-38`

```yaml
    ports:
      # Bind to loopback only — nginx terminates TLS and proxies to this port.
      - "127.0.0.1:${APP_PORT:-3002}:3000"
    mem_limit: 512m
    cpus: "1.0"
    healthcheck:
      test:
        [
          "CMD",
          "wget",
          "--no-verbose",
          "--tries=1",
          "--spider",
          "http://127.0.0.1:3000/robots.txt",
        ]
```

Tiga jenis nilai dalam lima belas baris. Entri port adalah string bertanda kutip, sesuai anjuran referensi Docker untuk port mapping, dan ${APP_PORT:-3002} di dalamnya diisi Compose setelah parsing. mem_limit: 512m tidak butuh tanda kutip karena 512m tidak cocok dengan aturan angka mana pun. test adalah flow list, kurung siku dan koma yang dipecah ke beberapa baris, dan hasil parsing-nya sama dengan list bertanda strip.

`.github/workflows/deploy.yml:7-10`

```yaml
on:
  push:
    branches: [main]
  workflow_dispatch:
```

on: adalah key yang dibaca GitHub untuk pemicu. Parser YAML 1.2 seperti js-yaml mempertahankannya sebagai on; PyYAML, parser YAML 1.1, mengembalikan key yang sama ini sebagai True. branches: [main] adalah flow list berisi satu item, dan workflow_dispatch: tidak punya apa-apa di belakangnya, jadi hasil parsing-nya null.

`.github/workflows/deploy.yml:213-222`

```yaml
      - name: Deploy over SSH
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.VPS_SSH_HOST }}
          username: ${{ secrets.VPS_SSH_USER }}
          key: ${{ secrets.VPS_SSH_KEY }}
          port: ${{ vars.VPS_SSH_PORT || 22 }}
          script: |
            set -euo pipefail
            cd "${{ vars.DEPLOY_PATH }}"
```

Tanda strip menjadikan step ini satu item di list steps, dan setiap key yang menjorok di bawahnya milik item itu. Setiap nilai ${{ }} adalah string biasa bagi YAML, meski tanpa tanda kutip, karena diawali tanda dolar; GitHub yang mengevaluasinya kemudian, termasuk ||. script: | menjaga setiap baris script shell tetap di barisnya sendiri, dengan dua belas spasi indentasinya dibuang.

## Yang sering rusak

- **found character that cannot start any token.** Ada tab yang bersembunyi di indentasi, misalnya hasil copy dari tempat lain atau diketik editor yang memakai tab. YAML melarang tab di sana, dan parser berhenti di baris itu. Aktifkan tampilan whitespace di editor, ganti tab dengan spasi, dan atur editor agar memakai spasi untuk indentasi YAML.
- **services.ports must be a mapping.** Sebuah key bergeser satu tingkat, sehingga sekarang menjadi milik induk yang lain. YAML-nya valid, karena itu parser diam saja dan tool justru protes soal nama yang tidak pernah Anda maksud sebagai service. Jalankan docker compose config untuk melihat pohon yang dibangun Compose, lalu sejajarkan key itu dengan saudaranya yang benar.
- **Nomor versi atau file mode yang berubah di tengah jalan.** version: 3.10 keluar sebagai 3.1 dari semua parser yang diuji, dan Compose merender entri environment 0755 sebagai 493. Tidak ada error; nilainya saja yang berbeda. Beri tanda kutip pada apa pun yang sebenarnya teks, walau terlihat seperti angka: "3.10", "0755".
- **Script tidak menemukan key on di workflow.** Tool buatan Anda membaca workflow dengan PyYAML, parser YAML 1.1, jadi on: kembali sebagai boolean True. GitHub sendiri tidak bermasalah; script Anda yang bermasalah. Pakai parser YAML 1.2 seperti js-yaml, atau cari pemicunya di bawah True kalau memang harus memakai PyYAML.
- **Nilai yang diawali *, !, atau #.** paths: **.js dibaca sebagai alias, dan kedua parser yang diuji gagal karenanya; if: !cancelled() dibaca sebagai tag YAML, hal yang diperingatkan referensi GitHub. Nilai yang diawali # adalah komentar, jadi berubah menjadi null. Beri tanda kutip pada nilainya, atau untuk kondisi di GitHub bungkus dengan ${{ }}.

### Nilai tanpa tanda kutip yang sama di tiga parser, diuji pada 2026-10-18 (nilai Compose dibaca dari field x- dengan docker compose config). Dengan tanda kutip, semuanya tetap berupa teks seperti yang ditulis.

| Di file | PyYAML 6.0.3 (YAML 1.1) | js-yaml 4.3 (YAML 1.2) | Docker Compose v5.5.1 |
| --- | --- | --- | --- |
| on: (sebagai key) | true | teks on | teks on |
| NO | false | teks NO | teks NO |
| yes | true | teks yes | teks yes |
| 0755 | 493 (oktal) | 755 (desimal) | 493 (oktal) |
| 3.10 | 3.1 | 3.1 | 3.1 |
| 22:22 | 1342 (basis 60) | teks 22:22 | teks 22:22 |
| #fff | null (komentar) | null (komentar) | null (komentar) |

## Coba sendiri

Anda butuh Python 3, Node.js dengan npx, serta Docker dengan Compose; di sini Compose hanya merender file dan tidak menjalankan apa pun. Setiap langkah mencetak apa yang benar-benar dibangun parser, jadi Anda melihat jebakannya sendiri, bukan sekadar percaya.

1. Buat folder lab dan sebuah file berisi delapan baris yang terlihat tidak berbahaya.

   ```bash
   mkdir yaml-lab && cd yaml-lab
   cat > traps.yaml <<'EOF'
   on: push
   country: NO
   debug: yes
   version: 3.10
   mode: 0755
   port: 22:22
   color: #fff
   quoted: "3.10"
   EOF
   ```
2. Baca dengan js-yaml, parser YAML 1.2, yang mencetak JSON. on, NO, dan yes tetap teks, tetapi version sudah menjadi 3.1, mode menjadi 755, dan color menjadi null.

   ```bash
   npx --yes js-yaml@4 traps.yaml
   ```
3. Baca dengan PyYAML, parser YAML 1.1, di virtual environment sekali pakai. Key on kembali sebagai true, NO sebagai false, yes sebagai true, 0755 sebagai 493, dan 22:22 sebagai 1342. Hanya quoted yang tetap "3.10".

   ```bash
   python3 -m venv .venv
   .venv/bin/pip install --quiet pyyaml
   .venv/bin/python -c 'import json, sys, yaml; print(json.dumps(yaml.safe_load(open(sys.argv[1])), indent=2))' traps.yaml
   ```
4. Sekarang tanyakan ke Docker Compose. Tulis compose.yaml dengan tiga jebakan di environment, lalu cetak apa yang akan dijalankan Compose: MODE menjadi "493", VERSION menjadi "3.1", dan DEBUG tetap "yes".

   ```bash
   cat > compose.yaml <<'EOF'
   services:
     demo:
       image: alpine:3
       environment:
         MODE: 0755
         VERSION: 3.10
         DEBUG: yes
       ports:
         - "127.0.0.1:${APP_PORT:-8080}:80"
   EOF
   docker compose config --format json
   ```
5. Interpolasi terjadi setelah parsing: port dirender sebagai 8080 secara default, sebagai 9090 kalau shell menyetel APP_PORT, dan sebagai teks mentah dengan --no-interpolate.

   ```bash
   docker compose config | grep published
   APP_PORT=9090 docker compose config | grep published
   docker compose config --no-interpolate | grep 127.0.0.1
   ```
6. Beri tanda kutip pada ketiga nilai itu lalu render ulang: MODE menjadi "0755", VERSION menjadi "3.10", dan DEBUG menjadi "yes", persis seperti yang ditulis.

   ```bash
   sed -i.bak -e 's/0755$/"0755"/' -e 's/3\.10$/"3.10"/' -e 's/yes$/"yes"/' compose.yaml
   docker compose config --format json | grep -A4 '"environment"'
   ```
7. Rusak indentasinya dengan sengaja. Setelah ports digeser dua spasi ke kiri, Compose menyebut services.ports must be a mapping. Kembalikan posisinya, lalu coba tab di depan image: parser berhenti sebelum Compose melihat apa pun.

   ```bash
   sed -i.bak 's/^    ports:/  ports:/' compose.yaml
   docker compose config
   sed -i.bak 's/^  ports:/    ports:/' compose.yaml
   printf 'services:\n  demo:\n\timage: alpine:3\n' > tab.yaml
   docker compose -f tab.yaml config
   ```
8. Bersihkan folder lab.

   ```bash
   cd .. && rm -rf yaml-lab
   ```

## Kuis singkat

1. **Referensi GitHub menyebut if: !cancelled() harus dibungkus ${{ }} atau diberi tanda kutip. Kenapa?**
   A. cancelled() bukan fungsi GitHub
   B. YAML membaca ! di depan sebagai tag, bukan teks
   C. if: hanya menerima kata true atau false
   Jawaban: B. ! adalah penanda tag di YAML, jadi parser membaca !cancelled() sebagai tag, bukan teks; kedua parser yang diuji menolaknya. Di dalam ${{ }} atau tanda kutip, ! hanyalah karakter dalam string, dan GitHub mengevaluasinya sebagai operator not.

2. **Baris mana yang memberi string 3.10 di semua parser YAML?**
   A. version: "3.10"
   B. version: 3.10
   C. version: 3.10 # biarkan teks
   Jawaban: A. Tanpa tanda kutip, 3.10 cocok dengan aturan float di YAML 1.1 maupun 1.2 dan berubah menjadi 3.1. Komentar tidak mengubah apa pun, karena komentar tidak berpengaruh pada pohon. Hanya tanda kutip yang menjadikannya string.

3. **File compose situs ini berisi - "127.0.0.1:${APP_PORT:-3002}:3000". Apa yang mengubah ${APP_PORT:-3002} menjadi 3002?**
   A. Parser YAML, saat membaca file
   B. Shell di server, sebelum Compose berjalan
   C. Docker Compose, setelah YAML selesai di-parse
   Jawaban: C. YAML tidak punya variabel, dan tidak ada shell yang membaca isi file compose. Compose yang melakukan interpolasi setelah parsing, memakai APP_PORT dari shell atau .env, atau 3002 kalau variabelnya tidak di-set atau kosong.

## Pikirkan dulu

- **Kenapa PyYAML mengubah key on: di workflow GitHub menjadi True, padahal GitHub membacanya dengan benar?** PyYAML adalah parser YAML 1.1, dan tipe boolean di YAML 1.1 mencakup on, off, yes, dan no. Core schema YAML 1.2 hanya menganggap true dan false sebagai boolean, jadi parser 1.2 mempertahankan on sebagai teks, dan GitHub membacanya sebagai key pemicu.
- **Apa beda script: | dan script: >?** | mempertahankan setiap pergantian baris, jadi script shell tetap satu perintah per baris. > melipat pergantian baris tunggal menjadi spasi, jadi baris-barisnya menjadi satu baris panjang. Keduanya menyisakan satu newline di akhir kecuali Anda menambahkan - untuk membuangnya.
- **image: dan ports: sama-sama milik web:. Apa yang harus benar soal indentasinya?** Keduanya menjorok lebih dalam dari web:, dengan jumlah spasi yang persis sama satu sama lain, dan memakai spasi, bukan tab. Geser salah satunya sejajar dengan web, maka ia malah menjadi saudara web.

## Pelajari lebih dalam

- [KYAML di Kubernetes 1.37: Subset YAML Stable dan kubectl -o kyaml](https://www.matthewswong.com/id/blog/kyaml-kubernetes-stable-yaml-format/)
- [Cara Deploy Docker Compose di Production untuk Pemula](https://www.matthewswong.com/id/blog/cara-deploy-docker-compose-production-vps/)
- [Cara Membuat CI/CD GitHub Actions untuk Pemula, 7 Langkah](https://www.matthewswong.com/id/blog/cara-membuat-ci-cd-github-actions-pemula/)
- [Reusable Workflow vs Composite Action di GitHub Actions](https://www.matthewswong.com/id/blog/github-actions-reusable-workflows-composite-actions/)

## Sumber

- [Spesifikasi YAML 1.2.2 (indentasi, komentar, block scalar, core schema)](https://yaml.org/spec/1.2.2/)
- [Tipe boolean YAML 1.1 (y, yes, on, off)](https://yaml.org/type/bool.html)
- [Tipe integer YAML 1.1 (oktal berawalan nol, basis 60)](https://yaml.org/type/int.html)
- [Tipe float YAML 1.1](https://yaml.org/type/float.html)
- [Dokumentasi Docker: interpolasi di Compose](https://docs.docker.com/reference/compose-file/interpolation/)
- [Dokumentasi Docker: mengatur variabel dengan interpolasi (.env, urutan prioritas)](https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/)
- [Dokumentasi Docker: fragment di Compose (anchor, alias, merge)](https://docs.docker.com/reference/compose-file/fragments/)
- [Dokumentasi Docker: extension di Compose (field x-)](https://docs.docker.com/reference/compose-file/extension/)
- [Dokumentasi Docker: referensi service Compose (tanda kutip untuk port dan boolean di environment)](https://docs.docker.com/reference/compose-file/services/)
- [Dokumentasi Docker: docker compose config](https://docs.docker.com/reference/cli/docker/compose/config/)
- [Dokumentasi Docker: cara kerja Compose (file Compose dan namanya)](https://docs.docker.com/compose/intro/compose-application-model/)
- [GitHub Docs: sintaks workflow (on, run, ! di kondisi if)](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax)
- [GitHub Docs: expression](https://docs.github.com/en/actions/concepts/workflows-and-actions/expressions)
- [GitHub Docs: script injection](https://docs.github.com/en/actions/concepts/security/script-injections)
- [GitHub Docs: memakai ulang konfigurasi workflow (anchor dan alias YAML)](https://docs.github.com/en/actions/reference/workflows-and-actions/reusing-workflow-configurations)
- [Changelog GitHub: Actions mendukung anchor YAML (2025-09-18)](https://github.blog/changelog/2025-09-18-actions-yaml-anchors-and-non-public-workflow-templates/)
- [GitHub Docs: build dan test Python (matrix versi bertanda kutip)](https://docs.github.com/en/actions/tutorials/build-and-test-code/python)
- [PyYAML: parser YAML 1.1 yang lengkap](https://pyyaml.org/wiki/PyYAML)
- [js-yaml: parser YAML 1.2 untuk JavaScript](https://github.com/nodeca/js-yaml)
- [go-yaml: package YAML untuk Go (catatan kompatibilitas YAML 1.1)](https://github.com/yaml/go-yaml)

---

- HTML version: https://www.matthewswong.com/id/learn/devops-from-zero/yaml-tutorial-for-beginners/
- English: https://www.matthewswong.com/en/learn/devops-from-zero/yaml-tutorial-for-beginners/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
