---
id: docker-yaml
title: "YAML"
shortTitle: "YAML"
track: docker
trackTitle: "Docker"
order: 12
level: core
status: published
estimatedMinutes: 30
prerequisites:
  - docker-compose
next: kubernetes-architecture
reviewedAgainst: 2026-10-19
sources:
  - title: "KodeKloud — Docker Training Course for the Absolute Beginner"
    url: https://learn.kodekloud.com/learn/courses/docker-training-course-for-the-absolute-beginner
  - title: "YAML 1.2 specification"
    url: https://yaml.org/spec/1.2.2/
  - title: "Compose file reference"
    url: https://docs.docker.com/reference/compose-file/
---

# YAML

`compose.yaml` ở bài Compose là một file YAML. Kubernetes dùng cùng kiểu file. Bài này đọc được mapping, list, và chỗ thụt lề làm Compose từ chối file.

## Để làm gì

Viết một file Compose ngắn mà không lẫn key với phần tử list, và chỉ ra vì sao một file trông đúng vẫn fail vì tab hoặc vì thiếu dấu cách sau dấu hai chấm.

## Gồm những gì

- Mapping: `khóa: giá trị`.
- List: dòng bắt đầu bằng `- `.
- List gồm nhiều mapping, kiểu một service có nhiều cổng, hoặc nhiều bước trong `command`.
- Thụt bằng dấu cách, cùng một bề rộng cho cùng một cấp. Không dùng tab.
- Thứ tự key trong mapping không đổi ý nghĩa. Thứ tự phần tử list thì đổi.
- Chú thích bắt đầu bằng `#`.

Bài này không dạy anchor, chuỗi nhiều dòng `|` / `>`, hay schema của từng trường Compose. Những trường đó nằm ở bài Compose và ở Kubernetes.

## Dùng khi nào

Trước khi sửa `compose.yaml` hoặc một manifest. File JSON cùng một dữ liệu vẫn chạy được ở nhiều chỗ, vì JSON đúng chuẩn cũng là YAML. File trong lab này viết dạng thụt lề, đúng kiểu Compose đang dùng.

## Kiến thức cốt lõi

Mapping là cặp khóa và giá trị. Khóa tra được, không cần nhớ vị trí. `services` trong Compose là mapping: khóa `web` trỏ tới cấu hình của service đó.

List là dãy có thứ tự. Không có khóa cho từng phần tử, chỉ có vị trí. `ports` và `command` là list.

```yaml
services:
  web:
    image: nginx:1.27-alpine
    ports:
      - "8080:80"
    command:
      - nginx
      - -g
      - daemon off;
```

`services` và `web` là mapping. `ports` có một phần tử. `command` có ba phần tử, và Docker chạy đúng thứ tự đó: chương trình `nginx`, rồi hai argument.

Cùng một cấp phải thẳng hàng. Con nằm sâu hơn cha, thường thêm hai dấu cách. Dòng `image` và dòng `ports` cùng cấp dưới `web`, nên cùng cột. Dòng `- "8080:80"` là phần tử của `ports`, nên sâu hơn `ports`.

Dấu hai chấm ngăn khóa và giá trị phải có dấu cách phía sau. `image:nginx:1.27-alpine` là một chuỗi, không phải khóa `image`. Viết `image: nginx:1.27-alpine`.

Tab không được dùng để thụt. Editor hay trộn tab và dấu cách khi file mở bằng hai kiểu. Compose báo lỗi ngay dòng đó.

Thứ tự các khóa `image`, `ports`, `command` đổi chỗ cho nhau thì service vẫn cùng một ý. Đảo `command` thành `- -g` rồi mới `- nginx` thì process nhận argument sai chỗ.

`#` bắt đầu chú thích đến hết dòng. `#` nằm trong chuỗi có nháy thì là ký tự thường, không phải chú thích.

<div class="net-flow">
<div class="net-step" style="--layer:#dbeafe"><b>Mapping</b><span>khóa, tra theo tên</span></div>
<span class="net-arrow" aria-hidden="true">→</span>
<div class="net-step" style="--layer:#e0e7ff"><b>List</b><span>dấu gạch, giữ thứ tự</span></div>
<span class="net-arrow" aria-hidden="true">→</span>
<div class="net-step" style="--layer:#dcfce7"><b>Thụt lề</b><span>dấu cách, cùng một cấp</span></div>
</div>

## Lệnh và code mẫu

File đúng, đặt ở `$HOME/devops-lab/docker/yaml/compose.yaml`:

```yaml
services:
  web:
    image: nginx:1.27-alpine
    ports:
      - "8080:80"
```

`docker compose config` đọc YAML và in lại file đã giải. Daemon không cần ở bước này nếu CLI Compose có mặt. Lệnh báo lỗi cú pháp thì chưa tới bước kéo image.

```bash
mkdir -p "$HOME/devops-lab/docker/yaml"
cd "$HOME/devops-lab/docker/yaml"
docker compose config
```

Một list gồm nhiều mapping, khi cần vài dịch vụ viết dạng danh sách thay vì khóa `web` / `db`. Compose thật dùng mapping `services`. Mẫu dưới chỉ để thấy hai kiểu:

```yaml
steps:
  - name: build
    command: docker build -t lab:1 .
  - name: run
    command: docker run --rm lab:1
```

`steps` là list. Mỗi phần tử là một mapping có `name` và `command`. Đưa `command` của `run` lên ngang hàng với `steps` thì hết list, YAML hiểu một khóa khác ở cấp gốc.

## Thực hành

1. Tạo `compose.yaml` như mẫu `services.web` ở trên.
2. Chạy `docker compose config`.
3. Đổi thụt của dòng `image` sang một tab, chạy lại. Ghi nhận lỗi.
4. Sửa lại bằng dấu cách. Xóa dấu cách sau dấu hai chấm của `image` thành `image:nginx:1.27-alpine`, chạy lại, rồi viết lại `image: nginx:1.27-alpine`.

## Kết quả mong đợi

- Lần `config` đầu in service `web`, image `nginx:1.27-alpine`, cổng published `8080` và target `80`. Compose khai thêm network mặc định. Chuỗi `8080:80` không còn nguyên dạng.
- File có tab thì lỗi `found character that cannot start any token`, trỏ đúng dòng thụt bằng tab.
- `image:nginx:1.27-alpine` thì Compose từ chối: `services.web must be a mapping`, hoặc `mapping values are not allowed in this context` nếu phía dưới còn khóa khác. Bản có dấu cách sau dấu hai chấm thì `config` in lại image đúng.

## Checklist

- [ ] Tra theo tên thì dùng mapping. Giữ thứ tự thì dùng list.
- [ ] Thụt bằng dấu cách, các dòng cùng cấp thẳng cột.
- [ ] Viết dấu cách sau dấu hai chấm của mỗi khóa.
- [ ] Đổi thứ tự `command` thì biết lệnh đổi. Đổi thứ tự khóa `image` và `ports` thì service không đổi ý.
- [ ] Chú thích bằng `#` ở đầu ý, ngoài chuỗi có nháy.

## Thẻ ôn

### docker-yaml-map-list
kind: distinguish
cue: `services.web` là mapping, tra theo tên. `ports` và `command` là list, mỗi phần tử một dòng `- `.

Q: Trong `compose.yaml`, `web` nằm dưới `services`, còn `"8080:80"` nằm dưới `ports`. Vì sao một bên không có dấu gạch, bên kia có?

A: `services` là mapping. `web` là khóa, Compose tìm service theo tên đó. `ports` là list. Mỗi cổng là một phần tử, mở đầu bằng `- `. Không có tên riêng cho từng cổng, chỉ có thứ tự trong list. Thiếu dấu gạch thì dòng cổng bị hiểu là khóa mới, không phải một cổng publish.

### docker-yaml-indent
kind: trap
cue: YAML thụt bằng dấu cách. Tab làm Compose từ chối file. Dòng cùng cấp phải cùng cột.

Q: `compose.yaml` trông đúng trên màn hình, nhưng `docker compose config` báo lỗi ngay dòng `image`. Editor đang trộn tab vào chỗ thụt. Sửa thế nào?

A: Xóa tab, thụt lại bằng dấu cách. Các khóa cùng cấp dưới `web`, như `image` và `ports`, phải cùng một cột. Phần tử của `ports` sâu hơn khóa `ports`. YAML không coi tab là thụt lề. Hai dòng lệch cột là hai cấp khác nhau, dù mắt thấy chúng thẳng hàng.

### docker-yaml-order
kind: distinguish
cue: Đổi chỗ các khóa của một service không đổi ý. Đổi thứ tự phần tử `command` thì đổi argument.

Q: Bạn cắt khóa `ports` lên trên khóa `image`. Lần khác bạn đảo list `command` từ `nginx`, `-g`, `daemon off;` thành `-g` đứng trước. Lần nào làm lệnh trong container đổi?

A: `image` và `ports` là hai khóa của cùng một mapping. Đổi vị trí khóa không đổi service. `command` là list. Phần tử đầu là chương trình, các phần tử sau là argument theo đúng thứ tự. Đưa `-g` lên trước thì `nginx` không còn là chương trình được gọi.

### docker-yaml-colon
kind: trap
cue: `image:nginx` thiếu dấu cách sau dấu hai chấm, nên không phải khóa `image`. Viết `image: nginx`.

Q: Dòng trong Compose ghi `image:nginx:1.27-alpine`. `docker compose config` không hiện trường image của service. Dòng đó đang là gì?

A: Không có dấu cách sau dấu hai chấm thì dòng đó là một chuỗi, không phải khóa `image`. Giá trị của `web` hết là một mapping, Compose báo `services.web must be a mapping`. Nếu dưới dòng đó còn khóa khác thì lỗi là `mapping values are not allowed in this context`. Viết `image: nginx:1.27-alpine`: một dấu cách ngay sau dấu hai chấm ngăn khóa và giá trị. Dấu hai chấm trong tag không cần thêm cách.

## Lỗi thường gặp

**Thụt bằng tab cho thẳng hàng với file khác.** Compose dừng ở dòng đó. Đổi cả file sang dấu cách.

**Quên `- ` trước cổng hoặc trước argument.** Dòng đó thành khóa, list mất phần tử.

**Đặt `#` giữa một chuỗi không nháy và tưởng đó vẫn là giá trị.** Từ `#` đến hết dòng là chú thích. Nháy chuỗi nếu dấu `#` thuộc giá trị.

**Sửa thứ tự `command` cho dễ đọc.** List giữ thứ tự. Khóa thì đổi chỗ được. Argument thì không.

## Nguồn

1. [KodeKloud — Docker for the Absolute Beginner](https://learn.kodekloud.com/learn/courses/docker-training-course-for-the-absolute-beginner) — Bonus Lecture: Introduction to YAML, và lab YAML. Bài viết lại, không chép lab.
2. [YAML 1.2](https://yaml.org/spec/1.2.2/) — mapping, list, thụt bằng dấu cách, chú thích `#`.
3. [Compose file](https://docs.docker.com/reference/compose-file/) — `services` là mapping, `ports` và `command` là list.

## Tiếp theo

[Kiến trúc Kubernetes](../kubernetes/01-architecture.md)
