Skip to content

YAML Cheat Sheet

YAML syntax on one page: lists, maps, quotes, multi-line strings, anchors, and real Docker Compose, GitHub Actions and Kubernetes files. Paste your own YAML into the checker to see what it really means and catch values that change on the way.

29 entries

Basics

Key and value

key: value
name: my-app
replicas: 3

The space after the colon is required. Keys are case-sensitive, so Name and name are two different keys.

Basics

Comments

# comment
# Settings for production
port: 8080  # after a value

Everything after # to the end of the line is ignored. After a value, the # needs a space before it. There are no block comments, so put # on every line.

Basics

Nesting with indentation

parent: child: value
server:
  host: localhost
  port: 8080

Indent with spaces, never tabs. Two spaces is the habit, but any number works as long as keys at the same level line up exactly.

Basics

Several documents in one file

---
---
kind: Service
---
kind: Deployment

Three dashes start a new document. Kubernetes uses this to keep several objects in one file. The --- before the first document is optional.

Lists & maps

List

- item
fruits:
  - apple
  - banana

A dash and a space start each item. The dashes may also sit level with the parent key instead of indented; both are valid.

Lists & maps

List of maps

- key: value key: value
users:
  - name: Ada
    role: admin
  - name: Lin
    role: dev

Each dash starts a new item. The item’s other keys line up with the first key after the dash, not with the dash.

Lists & maps

Inline list

[a, b]
ports: [80, 443]
branches: [main, develop]

Flow style, written like JSON. Handy for short lists; switch to dashes once it gets long.

Lists & maps

Inline map

{a: 1, b: 2}
limits: {cpu: 500m, memory: 128Mi}

Flow style for small maps. Any JSON object is also valid YAML, so {"a": 1} works too.

Lists & maps

Nested lists

- - a
matrix:
  - [1, 2]
  - [3, 4]
grid:
  - - a
    - b

A list inside a list, either inline or with a second dash. The inline form is easier to read.

Lists & maps

Empty list and empty map

[] {}
tags: []
labels: {}

Use these when the key must exist but hold nothing. A key with nothing after it is null, not an empty list.

Strings

Plain (unquoted) string

key: plain text
title: Hello world
url: https://example.com/path

Most text needs no quotes, even with spaces or a colon that is not followed by a space. Quote it when it looks like a number, true/false or null, or starts with a special character.

Strings

Double-quoted string

"double quotes"
message: "Line one\nLine two"
color: "#ff6600"

The only style where escapes such as \n, \t and \" work. Without quotes, " #" would start a comment and ": " a new key.

Strings

Single-quoted string

'single quotes'
path: 'C:\Users\me'
note: 'It''s fine'

Everything is taken literally, backslashes included, which suits Windows paths and regular expressions. Write a single quote as two: ''.

Strings

Text that looks like something else

key: "3.10"
python: "3.10"
zip: "02134"
answer: "yes"
port: "22:22"

Unquoted, 3.10 becomes 3.1 and 02134 loses its zero, and older parsers read yes as true and 22:22 as 1342. Quotes keep each one exactly as typed.

Multi-line

Literal block (keep line breaks)

key: |
script: |
  npm ci
  npm test

Every line break is kept and one newline is added at the end. The usual choice for shell scripts, certificates and SQL.

Multi-line

Folded block (join lines)

key: >
description: >
  This long sentence is
  wrapped in the file.

Lines are joined with spaces into one paragraph, with one newline at the end. A blank line inside the block becomes a real line break.

Multi-line

Drop the final newline

|- >-
key: |-
  -----BEGIN KEY-----
  abc123
  -----END KEY-----

The minus strips the newline at the end. Use it when a trailing \n would break the value, such as a token or a key.

Multi-line

Keep trailing blank lines

|+ >+
footer: |+
  Thanks,
  The team

next: value

The plus keeps every blank line at the end of the block. Rarely needed.

Types

Numbers

42 9.99 1e6
count: 42
price: 9.99
big: 1e6
hex: 0x1F
forever: .inf

Unquoted digits are numbers. 0x1F is 31, .inf is infinity and .nan is not-a-number. Quote anything that must stay text, such as phone numbers.

Types

Booleans

true false
enabled: true
debug: False

true and false in lower, Title or UPPER case. YAML 1.1 parsers (PyYAML, older Kubernetes tools) also treat yes, no, on and off as booleans.

Types

Null (empty)

null ~
owner: null
backup: ~
notes:

All three are null. A key with nothing after it is null too, which is easy to do by accident.

Types

Dates

2024-03-15
released: 2024-03-15
updated: 2024-03-15T09:30:00Z

YAML 1.2 keeps these as text. PyYAML, Ruby and other YAML 1.1 parsers turn them into date objects, so quote dates if the value must stay a string.

Types

Force a type with a tag

!!str !!int
port: !!str 8080
count: !!int "42"

A tag overrides the guess. Quotes are simpler for making text; tags are mainly seen in older files.

Reuse

Anchor and alias

&name *name
base_image: &img node:22
build:
  image: *img
test:
  image: *img

&name labels a value and *name repeats it. The anchor must come first in the file.

Reuse

Merge keys from an anchor

<<: *name
defaults: &defaults
  retries: 3
  timeout: 30
prod:
  <<: *defaults
  timeout: 60

Copies every key from the anchored map, then your own keys override them, so prod gets retries 3 and timeout 60. Docker Compose, GitLab CI and PyYAML support it; it is not part of YAML 1.2, so check your tool.

Reuse

Docker Compose extension fields

x-name: &name
x-logging: &logging
  driver: json-file
services:
  web:
    logging: *logging
  worker:
    logging: *logging

Compose ignores top-level keys that start with x-, which makes them a safe place to keep anchors.

Real files

Docker Compose file

services:
services:
  web:
    image: nginx:1.27
    ports:
      - "8080:80"
    environment:
      - TZ=Europe/London
    restart: unless-stopped

Ports are quoted on purpose: short values such as 22:22 could be read as a number by older parsers.

Real files

GitHub Actions workflow

on: jobs:
name: CI
on:
  push:
    branches: [main]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm test

Each step starts with a dash. Quote versions in a matrix ("3.10"), or 3.10 is read as 3.1.

Real files

Kubernetes Deployment

apiVersion: kind:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: nginx:1.27

containers is a list of maps, so it needs the dash. Labels under selector and template must match.

YAML Syntax Rules

YAML is built from two things: maps (key: value pairs) and lists (lines starting with - ). Indentation shows what belongs to what, the way brackets do in JSON. Almost every broken YAML file breaks one of these rules:

YAML syntax rules with right and wrong examples
RuleRightWrong
A space after every colonport: 8080port:8080
A space after every list dash- apple-apple
Indent with spaces onlyspacesa tab character
Keys at the same level line upsame number of spacesone key a space further in
Each key once per mapone port: keytwo port: keys
A space before a trailing commentport: 80 # webport: 80# web

Keys and values are case-sensitive, and the file can be called .yaml or .yml. Any JSON is also valid YAML, so {"port": 8080} works anywhere YAML is expected.

Multi-line Strings: | vs >

Put | or > after the colon and indent the text on the lines below. The pipe keeps your line breaks; the greater-than sign joins the lines into one. A - or + after either one decides what happens to the newline at the very end. Here is the same two-line block with each marker:

text: |
  line one
  line two
What each block scalar marker produces
MarkerResultUse it for
|"line one\nline two\n"Scripts, certificates, SQL
|-"line one\nline two"Keys and tokens that must not end in \n
|+Like |, plus any blank lines after the blockText where trailing blank lines matter
>"line one line two\n"Long descriptions wrapped in the file
>-"line one line two"One long line, such as a long command

The block ends at the first line that is indented less than the text. Inside a block, #, quotes and colons are just text, which is why | is the easy way to paste a shell script or a private key into YAML.

When Do You Need Quotes?

Most strings need no quotes. You need them when the text would otherwise be read as something else: a number, a boolean, null, a comment or a new key. These are the values that cause trouble:

Unquoted values that are not read as text
You writeIt is read asWrite instead
version: 3.10the number 3.1version: "3.10"
zip: 02134the number 2134zip: "02134"
country: NOfalse in YAML 1.1 parsers (the “Norway problem”)country: "NO"
port: 22:221342 in YAML 1.1 parsers (base 60)port: "22:22"
color: #fffnull, because # starts a commentcolor: "#fff"
title: Note: read mean error: a second “: ”title: "Note: read me"
user: @adminan error: @ and ` cannot start a valueuser: "@admin"
value: nullnull, not the wordvalue: "null"

Double quotes understand escapes such as \n and \". Single quotes take everything literally, so 'C:\Users' stays as typed; write a single quote inside them as two ('It''s'). If you are not sure whether a value needs quotes, quoting it is always safe.

Worked Example: the Python Version That Became 3.1

A GitHub Actions workflow tests three Python versions, and the 3.10 job fails because it cannot find Python 3.1:

strategy:
  matrix:
    python-version: [3.9, 3.10, 3.11]
  1. 3.10 has no quotes and looks like a number, so YAML reads it as a number.
  2. As a number, 3.10 is the same as 3.1. The trailing zero is gone before GitHub ever sees the file.
  3. The setup step is asked for Python 3.1, which does not exist. 3.9 and 3.11 survive only because they have no trailing zero.

The fix is to quote every version, so all three stay text:

    python-version: ["3.9", "3.10", "3.11"]

Press Example in Check YAML mode to see the checker catch this along with four other values that change without warning. The same thing happens to Node versions such as 20.10 and to any version number ending in zero.

Common YAML Errors and What They Mean

Error messages differ between tools. These are the ones from PyYAML (Python, Ansible) and similar parsers that people search for most, and the usual cause of each:

Common YAML error messages and their causes
ErrorUsual cause
mapping values are not allowed hereA value contains “: ”, or a line is indented too far. Quote the value or fix the indent.
found character '\t' that cannot start any tokenA tab is used for indentation. Replace it with spaces.
did not find expected keyA line is out of line with its siblings, or a quote was opened and never closed.
could not find expected ':'A line of text with no key, often the second line of a value that needed |.
found character '@' that cannot start any tokenA value starts with @ or `. Quote it.
found undefined aliasA *name is used before its &name anchor, or the names differ.
duplicated mapping keyThe same key appears twice in one map. PyYAML silently keeps the last one instead of failing.

The line number in the message is where the parser gave up, which can be a line or two after the real mistake. Look at the line above as well, and paste the file into the checker to get the exact line and column.

Frequently asked questions

What is the difference between .yaml and .yml?

None. Both are the same format and every tool reads both. The YAML project recommends .yaml, but some tools look for one name specifically, such as docker-compose.yml or .github/workflows/*.yml, and accept either.

Can YAML use tabs?

Not for indentation. A tab at the start of a line is an error, so use spaces. Tabs are fine inside a value, such as inside quotes or a | block.

How do I comment out several lines in YAML?

YAML has no block comments, so put # at the start of each line. Most editors do this for you: select the lines and press Ctrl+/ (Cmd+/ on a Mac).

Is YAML a superset of JSON?

For practical purposes, yes. Since YAML 1.2, any JSON file is valid YAML, so you can paste JSON into a YAML file or mix the styles, such as ports: [80, 443] inside an indented file.

Sources