cheat sheet

YAML Cheat Sheet

This YAML cheat sheet contains every syntax, every element - searchable, filterable by version, copy-ready, with parsed-output previews.

/ to focus
version:
Document Structure
ALL
basic
Document start marker
---
Document end marker
...
Multiple documents in one file (YAML stream)
--- name: Alice --- name: Bob ...
Comments (ignored by all parsers)
# This is a comment key: value # inline comment
VS Code YAML schema hint (yaml-language-server)
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json name: CI
Key-Value Pairs (Mappings)
ALL
basic
Simple key-value pair
name: Alice
Multiple key-value pairs
name: Alice age: 30 city: London
Nested mapping (2-space indent convention)
person: name: Alice age: 30 address: city: London zip: EC1A
Flow mapping (inline, JSON-style)
person: {name: Alice, age: 30}
Sequences (Lists)
ALL
basic
Block sequence (one item per line)
fruits: - apple - banana - cherry
Flow sequence (inline)
fruits: [apple, banana, cherry]
Sequence of mappings
users: - name: Alice role: admin - name: Bob role: viewer
Indentation Rules
ALL
note
Spaces only, never tabs
parent: child: value # 2 spaces nested: deep: value # 4 spaces total
Any number of spaces is valid - just be consistent
a: b: 3-space indent c: also valid
Docker Compose
real-world
docker-compose.yml with env vars and reusable base
version: "3.9" x-common: &common restart: unless-stopped logging: driver: json-file services: web: <<: *common image: nginx:latest ports: - "${WEB_PORT:-80}:80" volumes: - ./html:/usr/share/nginx/html depends_on: - api api: <<: *common build: ./api environment: NODE_ENV: ${NODE_ENV:-production} PORT: ${API_PORT:-3000} volumes: db_data:
Kubernetes Deployment
real-world
Deployment manifest skeleton
apiVersion: apps/v1 kind: Deployment metadata: name: my-app labels: app: my-app spec: replicas: 3 selector: matchLabels: app: my-app template: metadata: labels: app: my-app spec: containers: - name: app image: myapp:latest ports: - containerPort: 3000 env: - name: NODE_ENV value: production - name: DB_PASS valueFrom: secretKeyRef: name: db-secret key: password
Kubernetes ConfigMap
real-world
ConfigMap with embedded YAML file
apiVersion: v1 kind: ConfigMap metadata: name: app-config data: APP_ENV: production LOG_LEVEL: info config.yaml: | server: port: 8080 database: pool_size: 10
GitHub Actions
real-world
CI/CD workflow with env vars and matrix
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json name: CI on: push: branches: [main] pull_request: branches: [main] env: NODE_VERSION: "20" REGISTRY: ghcr.io jobs: test: runs-on: ubuntu-latest strategy: matrix: node: [18, 20, 22] steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node }} - run: npm ci - run: npm test
Ansible Playbook
real-world
Playbook with variables and loop
--- - name: Configure web server hosts: webservers become: true vars: http_port: "{{ lookup('env', 'HTTP_PORT') | default('80') }}" packages: - nginx - certbot tasks: - name: Install packages apt: name: "{{ packages }}" state: present - name: Start nginx service: name: nginx state: started enabled: true
Front Matter (Jekyll / Hugo / Astro)
real-world
Static site generator front matter
--- title: "My Post Title" date: 2024-06-01 draft: false tags: - yaml - devops categories: - guides description: "A short intro to YAML." ---
OpenAPI / Swagger
real-world
OpenAPI 3.0 skeleton
openapi: "3.0.3" info: title: My API version: "1.0.0" paths: /users: get: summary: List users parameters: - name: limit in: query schema: type: integer default: 20 responses: "200": description: Success content: application/json: schema: type: array items: $ref: "#/components/schemas/User" components: schemas: User: type: object properties: id: type: integer name: type: string
Environment Variable Substitution
real-world
Docker Compose / shell-style env var with default
port: ${PORT:-8080} host: ${HOST:-localhost}
Required variable (error if unset)
api_key: ${API_KEY:?API_KEY must be set}
Ansible / Jinja2 variable with filter
port: "{{ lookup('env', 'PORT') | default('8080') }}"
Helm values.yaml with template reference
# values.yaml replicaCount: 1 image: repository: nginx tag: "{{ .Chart.AppVersion }}"
Strings
ALL
basic
Plain (unquoted) string
name: Hello World
Single-quoted (no escape processing)
path: '/usr/local/bin'
Double-quoted (escape sequences allowed)
message: "Hello\nWorld"
Single quote inside single-quoted string (doubled)
note: 'it''s a value'
Force string for values that look like other types
version: "1.0" zip_code: "01234" yes_word: "true" date_str: "2024-01-01"
Numbers
ALL
basic
Integer
port: 8080
Float
ratio: 3.14
Scientific notation
distance: 1.5e10
Hexadecimal (YAML 1.2: prefix 0x)
color: 0xFF5733
Octal (YAML 1.2: prefix 0o)
permissions: 0o755
Octal (YAML 1.1 style - avoid in new files)
permissions: 0755
Infinity and NaN
max: .inf min: -.inf undef: .nan
Booleans
1.21.1
basic
YAML 1.2: only true / false (case-insensitive)
enabled: true active: True disabled: false
YAML 1.1 boolean synonyms (avoid in new files)
a: yes # true b: no # false c: on # true d: off # false e: y # true f: n # false
Null
ALL
basic
Explicit null
middle_name: null nickname: ~
Implicit null (empty value)
optional_field:
Dates and Timestamps
ALL
basic
Date (ISO 8601)
date: 2024-01-15
Datetime with UTC (Z suffix)
created_at: 2024-01-15T10:30:00Z
Datetime with UTC offset
event: 2024-06-01T09:00:00+02:00
Force date as string (always quote)
label: "2024-01-15"
Escape Sequences (double-quoted only)
ALL
note
Newline \n
"line1\nline2"
Tab \t
"col1\tcol2"
Backslash
"C:\\Users\\Alice"
Unicode codepoint
"caf\u00E9"
Nested Mappings
ALL
basic
App config with multiple service blocks
app: server: host: localhost port: 3000 database: host: db.local port: 5432 name: mydb
Sequences of Complex Objects
ALL
basic
List of service objects
services: - name: web image: nginx:latest ports: - "80:80" - name: api image: node:20 ports: - "3000:3000"
Complex Keys
ALL
advanced
Key with spaces (quoted)
"my key": value
Explicit complex key with ? indicator
? [host, port] : connection string
Merging Mappings with <<
1.1
advanced
Merge key (officially YAML 1.1 only, widely supported)
defaults: &defaults color: blue size: medium widget: <<: *defaults color: red # overrides merged value
Merge from multiple anchors
base: &base env: production extras: &extras debug: false combined: <<: [*base, *extras] name: app
Literal Block Scalar ( | ) - keeps newlines
ALL
block
Default: one trailing newline (clip)
message: | Hello Alice, Welcome aboard. Regards, Team
Strip trailing newline ( |- )
script: |- echo "start" echo "done"
Keep all trailing newlines ( |+ )
text: |+ Line one Line three
Folded Block Scalar ( > ) - folds newlines to spaces
ALL
block
Newlines become spaces (wrapping)
description: > This is a long description that wraps across lines but becomes a single paragraph.
Blank line inside folded = real paragraph break
body: > First paragraph. Second paragraph.
Folded + strip ( >- )
summary: >- Short but wrapped across two lines.
Chomping Quick Reference
ALL
note
| or > (clip) - exactly one trailing newline
text: | content
|- or >- (strip) - no trailing newline
text: |- content
|+ or >+ (keep) - all trailing newlines preserved
text: |+ content
Explicit indentation indicator (useful when content starts with spaces)
text: |2 two-space indented content here
The DRY Problem Anchors Solve
ALL
note
Before anchors (repeated)
staging: timeout: 30 retries: 3 log: json production: timeout: 30 retries: 3 log: json
After anchors (DRY)
defaults: &defaults timeout: 30 retries: 3 log: json staging: <<: *defaults production: <<: *defaults
Anchors and Aliases
ALL
advanced
Define anchor with &, reference with *
default_env: &default_env LOG_LEVEL: info TIMEOUT: 30 staging: env: *default_env production: env: *default_env
Anchor on a scalar value
base_image: &img node:20-alpine services: api: image: *img worker: image: *img
Anchor on a sequence
common_ports: &ports - 80 - 443 service_a: ports: *ports service_b: ports: *ports
Merge with Override
1.1
advanced
Merge base and override individual keys
base: &base restart: always logging: driver: json-file web: <<: *base image: nginx ports: ["80:80"] api: <<: *base image: myapp restart: on-failure # overrides merged value ports: ["3000:3000"]
Type Tags
ALL
advanced
Force string with !!str
code: !!str 404 flag: !!str true
Force integer with !!int
count: !!int "42"
Force float with !!float
price: !!float "9"
Binary data with !!binary (base64)
image: !!binary | R0lGODlhAQABAAAAACH5BAEK
Sets and Ordered Mappings
ALL
advanced
Set (unique keys, null values)
allowed_roles: !!set ? admin ? editor ? viewer
Ordered mapping (omap)
steps: !!omap - checkout: pull from git - build: run make - test: run pytest
YAML Directives
ALL
advanced
YAML version directive
%YAML 1.2 --- key: value
TAG directive (namespace alias)
%TAG !custom! tag:myapp.io,2024: --- config: !custom!settings debug: true
Special Characters and Gotchas
ALL
note
Colon in value must be quoted
url: "https://example.com" ratio: "1:2"
Value starting with { or [ must be quoted
json_str: "{\"key\":\"value\"}" list_str: "[1, 2, 3]"
Value starting with # must be quoted (else it is a comment)
color: "#FF5733"
Tabs forbidden for indentation
# WRONG (tab character before child): # key:[TAB]value # CORRECT (spaces): parent: child: value
YAML Stream Processing
ALL
advanced
Multiple documents - iterate with load_all (Python)
--- kind: Deployment name: web --- kind: Service name: web-svc --- kind: Ingress name: web-ingress
Apply all docs in a stream with kubectl
kubectl apply -f multi-resource.yaml # or pipe from stdin: cat *.yaml | kubectl apply -f -

What Is YAML?

YAML is a human-readable data serialization format used to represent structured data in plain text. The acronym stands for "YAML Ain't Markup Language," a recursive name that signals its creators didn't want it treated as just another document markup language.

Clark Evans, Ingy döt Net, and Oren Ben-Kiki created YAML in 2001, drawing influence from Python's indentation-based syntax, Perl's data-handling modules, and the broader XML community's push for simpler alternatives.

Key distinction from XML and JSON: YAML uses indentation and plain text to define structure. No angle brackets. No curly braces required at the top level. What you write looks close to how you'd describe data verbally.

YAML files use either .yaml or .yml as file extensions. Both are interchangeable. Most tooling accepts either.

Over 60% of developers now use YAML in DevOps workflows, according to a 2025 survey cited by MoldStud, reflecting how central the format has become to modern infrastructure tooling.

Where YAML Is Used

Primary use cases:

  • Configuration files for Kubernetes, Docker Compose, and Ansible

  • CI/CD workflow definitions in GitHub Actions and GitLab CI

  • Infrastructure as code manifests

  • Data exchange between systems and services

  • OpenAPI specification files

Kubernetes alone had 5.6 million developers using it globally as of 2024 (CNCF), and every Kubernetes manifest is written in YAML. That gives you a rough sense of the format's reach.

YAML vs. Other Formats at a Glance

Feature

YAML

JSON

TOML

Comments

Yes (#)

No

Yes

Readability

High

Medium

High

Multiline strings

Yes (native)

Limited

Yes

Type coercion

Implicit (tricky)

Explicit

Explicit

Best for

Deep config, IaC

APIs, data transfer

Flat config files

Superset of JSON

Yes

No

No


What Are the Core YAML Data Types?

YAML supports 6 native scalar types: strings, integers, floats, booleans, null, and dates. The parser detects the type automatically based on the value's format, which is useful but can cause unexpected behavior if you're not careful.

Strings

Strings are the most flexible type in YAML. They don't require quotes in most cases. But there are situations where quoting is mandatory.

When you must quote a string:

  • The value starts with a special character (:, {, }, [, ], ,, #, &, *, ?, |, >, !, %, @, `)

  • The value could be misread as another type ("true", "null", "1.0")

  • The value contains a colon followed by a space

Single quotes preserve the literal string exactly. Double quotes allow escape sequences like \n (newline) and \t (tab).

plain: This is a plain string
single: 'Literal backslash: \n stays as \n'
double: "Escape works here: \n becomes a newline"

Numbers and Booleans

Integers are written as bare numbers: 42, -7, 0.

Floats follow standard decimal notation: 3.14, -0.5, 1.0e3.

Booleans accept true and false. Here's the tricky part: YAML 1.1 (still used by PyYAML, which is common) also treats yes, no, on, off as booleans. YAML 1.2 removed that behavior, but parser inconsistency means you shouldn't rely on it either way.

The "Norway Problem" is real. The country code NO parses as boolean false in YAML 1.1 parsers. Always quote country codes and similar values.

count: 42
ratio: 0.75
enabled: true
country: "NO"   # quoted to prevent boolean coercion

Null and Date Values

Null is represented as null or the tilde ~. An empty value also resolves to null.

Dates follow ISO 8601 format. An unquoted 2024-01-15 becomes a date object in many parsers, not a string. Wrap it in quotes if you need it as text.

missing: null
also_missing: ~
bare_missing:
date_object: 2024-01-15
date_string: "2024-01-15"

How Does YAML Indentation and Syntax Work?

Indentation defines structure in YAML. There are no closing tags, no brackets to match. The hierarchy comes entirely from how far each line is indented.

The single hardest rule for most people new to YAML: spaces only, never tabs. A single tab character will throw a ScannerError. Most editors handle this automatically, but Vim users running default settings have hit this more than once.

The Core Syntax Rules

2-space indentation is the community standard. 4-space works. What matters is consistency within a file.

Key syntax elements:

  • Key-value pairs use a colon and a space: key: value

  • Comments start with # and can appear inline after a value

  • Document start marker is --- (optional but conventional)

  • Document end marker is ... (optional, used in streaming contexts)

  • YAML is case-sensitive: True, true, and TRUE behave differently in some parsers

---
# This is a comment
app_name: my-service   # inline comment
port: 8080
debug: true

Indentation Errors in Practice

Nearly 40% of developers have encountered parsing failures from indentation mistakes, according to Stack Overflow survey data cited by MoldStud.

That number makes sense. A single misplaced space shifts everything below it in the hierarchy. There's no closing bracket to catch the error visually. You often don't find out until the parser runs.

Use yamllint or the Red Hat YAML extension for VS Code to catch these before they cause problems. Both tools flag indentation issues immediately.


What Is the YAML Scalar Syntax?

A scalar is any single value in YAML. Strings, numbers, booleans, and dates are all scalars. The format gives you 3 distinct ways to write them, each with different behavior for whitespace and newlines.

Literal Block Scalar (|)

The pipe character preserves newlines exactly as written. Each newline in the block becomes a newline in the output. Useful for embedding shell scripts, certificates, or any content where line breaks matter.

script: |
  #!/bin/bash
  echo "Starting deployment"
  cd /app && npm start

Chomping indicators control trailing newlines:

  • | keeps one trailing newline (default)

  • |- strips all trailing newlines

  • |+ keeps all trailing newlines

Folded Block Scalar (>)

The > character folds newlines into spaces. Single line breaks inside the block become spaces. Blank lines become actual newlines. Good for long strings that need to stay readable in the YAML file but exist as a single line in the output.

description: >
  This is a long description
  that wraps across multiple lines
  but becomes one line in output.

The output of the above is: This is a long description that wraps across multiple lines but becomes one line in output.\n

Quoted Scalars

Single-quoted and double-quoted scalars sit on one line (unless deliberately continued). Double quotes support escape sequences. Single quotes don't.

Style

Escape sequences

When to use

Plain

No

Simple values with no special characters

Single '...'

No

Values where \n should be literal

Double "..."

Yes

Values needing \n, \t, Unicode escapes

Literal |

N/A

Multiline content, preserve newlines

Folded >

N/A

Multiline content, fold to single line


How Are YAML Mappings (Key-Value Pairs) Written?

Mappings are the fundamental YAML structure. A mapping is a collection of key-value pairs where each key is unique within its scope. Most YAML files you'll ever work with are primarily made up of mappings.

Basic and Nested Mappings

The basic form is one key-value pair per line:

name: my-app
version: "1.0.0"
port: 3000

Nested mappings use indentation to create parent-child relationships:

database:
  host: localhost
  port: 5432
  name: production_db
  credentials:
    user: admin
    password: "secret"

database is the parent key. host, port, name, and credentials are its children. credentials is itself a mapping with two children.

Flow Mappings

Flow mappings write key-value pairs on a single line using curly braces. Functionally identical to block mappings. Better for short, simple structures where vertical space matters.

server: {host: localhost, port: 8080, debug: false}

Merging Mappings With <<

The merge key (<<) lets one mapping inherit the key-value pairs of another. Commonly used with anchors.

defaults: &defaults
  timeout: 30
  retries: 3

production:
  <<: *defaults
  host: prod.example.com

production inherits timeout and retries from defaults, then adds its own host. The merge key reduces repetition across similar config blocks. It's especially useful in Docker Compose files where multiple services share base configurations.

Limitation: Merge keys are file-scoped. They cannot pull from another YAML file.


How Are YAML Sequences (Lists) Written?

A sequence is an ordered list of items. Unlike mappings, items don't have unique keys. The position in the list is what matters.

59% of professional developers use Docker (Stack Overflow, 2024), and Docker Compose sequences are where most people first encounter YAML lists in production configs.

Block Sequences

Each item in a block sequence starts with a dash and a space (- ). The dash is part of the indentation.

services:
  - api
  - worker
  - scheduler

Common mistake: forgetting that the dash counts as part of the indentation. This breaks:

app:
  name: my-app
services:
- api       # wrong: not indented under anything

Flow Sequences

Flow sequences use square brackets. Inline. Good for short lists of simple values.

ports: [3000, 8080, 9090]
tags: ["web", "production", "v2"]

Sequences of Mappings

This is the pattern used in Kubernetes pod specs, GitHub Actions steps, and Ansible tasks. Each list item is itself a mapping.

steps:
  - name: Install dependencies
    run: npm install
  - name: Run tests
    run: npm test
  - name: Deploy
    run: ./deploy.sh
    env:
      NODE_ENV: production

Each item starts with - followed by the first key of that item's mapping. Subsequent keys in the same item are aligned with the first key, not the dash.


What Are YAML Anchors and Aliases?

Anchors and aliases are YAML's built-in mechanism for reusing content. Define a block once, reference it anywhere else in the same file. No duplication, no drift between repeated sections.

Over 93% of professional teams track configuration files in Git (Stack Overflow 2024, cited by MoldStud). Anchors reduce the lines that need tracking in the first place.

Anchor and Alias Syntax

Anchor: &anchor_name marks a reusable block.

Alias: *anchor_name inserts the anchored block in its place.

base_config: &base
  image: node:18-alpine
  restart: unless-stopped
  environment:
    NODE_ENV: production

api:
  <<: *base
  ports:
    - "3000:3000"

worker:
  <<: *base
  command: node worker.js

Both api and worker inherit from base_config. If you change the base image, you change it once. Both services pick it up.

Extending With the Merge Key

The << merge key inserts the aliased content and allows the receiving mapping to override specific keys.

defaults: &defaults
  replicas: 1
  memory: 256Mi

api:
  <<: *defaults
  replicas: 3          # overrides the anchor value
  memory: 512Mi        # overrides the anchor value

2 hard limits to know:

  • Anchors are file-scoped. *base from file-a.yaml cannot be referenced in file-b.yaml.

  • Circular anchors are not allowed. An anchor cannot reference itself.

GitHub Actions uses anchors heavily in complex workflow files to share step definitions across jobs. Ansible playbooks use the same pattern for shared task variables.

How Do YAML Multi-Document Files Work?

A single .yaml file can contain multiple separate YAML documents, each divided by ---. This is standard practice in Kubernetes, where a Deployment and its Service often ship in one file.

The --- marker signals the start of a new document. The ... marker signals an explicit end. Both are optional in most contexts, but --- at the top of every document is a widely followed convention.

Separating Documents With ---

Standard multi-document layout:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 2

---
apiVersion: v1
kind: Service
metadata:
  name: my-app-service
spec:
  selector:
    app: my-app
  ports:
    - port: 80

kubectl apply -f processes both documents in one command. Tools like ArgoCD and Helm read every resource in a multi-document file and track them as a single application.

Parser Support for Multi-Document Files

Not all parsers handle multi-document YAML automatically.

Behavior by tool:

  • kubectl: Reads all documents in a file natively

  • PyYAML: Requires yaml.safe_load_all(), not yaml.safe_load()

  • Terraform: The yamldecode() function fails on multi-document files (known issue)

  • js-yaml: Supports multi-document via loadAll()

The ... end-of-document marker matters most in streaming contexts. YAML parsers processing a continuous data stream use it to know when one document ends and the next hasn't started yet.


What Are the Most Common YAML Errors and How to Fix Them?

Over 70% of issues in YAML configuration files stem from indentation problems, according to MoldStud's analysis of YAML parsing failures. The format's reliance on whitespace makes it unusually sensitive to mistakes that would be invisible in other formats.

Tab vs. Space Errors

Tabs are illegal in YAML. A single tab character throws a ScannerError that looks like this:

yaml.scanner.ScannerError: mapping values are not allowed here

The error message rarely says "tab detected." It just points to the line where the parser got confused, which is usually one or two lines after the actual tab.

Fix: Set your editor to insert spaces automatically. In VS Code, the YAML extension from Red Hat catches this immediately. yamllint flags it before runtime.

Missing Space After Colon

key:value is not valid YAML. key: value is.

The colon-space combination is the delimiter. Without the space, the parser treats the entire string as a plain scalar, not a key-value pair.

# Wrong
port:8080

# Right
port: 8080

Boolean and Type Coercion Issues

A 2024 survey found 35% of YAML parsing issues stem from incorrect string declaration, according to MoldStud research.

Value

YAML 1.1 result

YAML 1.2 result

Safe fix

yes / no

Boolean

String

Quote it: "yes"

NO

false

String

Quote it: "NO"

1.10

Float 1.1

Float 1.1

Quote it: "1.10"

2024-01-15

Date object

Date object

Quote it: "2024-01-15"

Unquoted Special Characters

Several characters have reserved meaning in YAML when unquoted: :, #, {, }, [, ], ,, &, *, ?, |, >, !, %, @.

A value starting with any of these needs quotes. The most common offender is : inside a URL.

# Wrong
url: https://example.com/api

# Right
url: "https://example.com/api"

Wrong Nesting Level

Shifting a block one indentation level too deep or too shallow creates a completely different data structure with no error. The parse succeeds, but the data is wrong.

Validate with yamllint or use the YAML Validator to catch structural issues before they reach a runtime environment.


How Does YAML Compare to JSON and TOML?

YAML, JSON, and TOML each have a clear domain where they work best. No single format wins across all use cases.

YAML is a superset of JSON. Every valid JSON file is also valid YAML. You can feed {"key": "value"} to any YAML parser and it will parse correctly.

Dimension

YAML

JSON

TOML

Comments

Yes

No

Yes

Multiline strings

Native

Requires \n escapes

Native

Type coercion

Implicit (risky)

Explicit (safe)

Explicit (safe)

Parser speed

Slower

Fastest

Middle

Deep nesting

Clean

Bracket-heavy

Gets messy

Best for

DevOps config, IaC

APIs, data exchange

Flat app config

YAML vs. JSON

JSON has no comment syntax. That's the dealbreaker for configuration files, where you need to explain why a value is set a certain way.

JSON also requires quoting every key, uses brackets everywhere, and has no multiline string support without escape sequences.

JSON parsers are significantly faster than YAML parsers. For API payloads processed at scale, JSON wins on performance. A RESTful API returning thousands of responses per second doesn't belong in YAML.

YAML vs. TOML

TOML is excellent for flat, human-written configuration. Rust's Cargo.toml, Python's pyproject.toml, and Hugo's config files all use it.

TOML avoids YAML's implicit type coercion entirely. yes is always the string "yes", never a boolean. That removes an entire category of bugs.

TOML gets messy with deep nesting. A 5-level Kubernetes resource structure would be painful to express in TOML but is natural in YAML.


What Are the YAML Tags and Custom Types?

YAML's !! prefix forces explicit type assignment. You use it when the parser's auto-detection would produce the wrong type, or when a specific application needs a custom type.

Tags follow the format !!type_name. Standard YAML tags cover all 6 native types. Language-specific tags extend beyond that.

Standard !! Tags

Built-in YAML type tags:

  • !!str forces a value to string: !!str 123"123", not integer 123

  • !!int forces integer parsing

  • !!float forces float parsing

  • !!bool forces boolean interpretation

  • !!null forces null regardless of value

  • !!seq forces sequence (list) type

  • !!map forces mapping (dict) type

The most common use case is preventing date auto-detection: !!str 2024-01-15 stays as a string.

Python-Specific Tags in PyYAML

PyYAML adds !!python/object tags that serialize arbitrary Python objects to YAML. Real Python's documentation shows the !! prefix is how YAML resolves "potential ambiguities" by casting values to specific data types.

# Standard type override
port: !!str 8080
enabled: !!str true
date: !!str 2024-01-15

# Python-specific (PyYAML only)
custom: !!python/object:mymodule.MyClass
  attr: value

YAML 1.1 vs. YAML 1.2 Type Resolution

YAML 1.2 tightened implicit type resolution significantly. In YAML 1.1, yes, no, on, off, true, false all resolve to booleans. YAML 1.2 limits booleans to true and false only.

PyYAML still implements YAML 1.1 by default, which catches many developers off guard. The ruamel.yaml library supports YAML 1.2 if you need strict compliance.


How Is YAML Used in Real-World DevOps Tools?

YAML is the primary configuration language for the tools that run modern software development infrastructure. Understanding how each tool uses it differently saves real debugging time.

Ansible is the most widely used configuration management tool, according to Stack Overflow's 2024 Developer Survey. Docker is used by 59% of professional developers (Stack Overflow, 2024). Kubernetes production deployments jumped from 66% to 80% of organizations between 2023 and 2024 (CNCF). GitHub Actions runs over 5 million workflows every day.

Every one of those tools runs on YAML files.

Kubernetes Manifests

Every Kubernetes resource is defined as a YAML document. The structure is always the same: 4 top-level keys.

apiVersion: apps/v1       # API group and version
kind: Deployment          # Resource type
metadata:                 # Name, namespace, labels
  name: my-app
  namespace: production
spec:                     # Desired state
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: app
          image: my-app:v1.2.0
          ports:
            - containerPort: 3000

apiVersion and kind together determine which Kubernetes API handles the resource. metadata.name must be unique within a namespace. spec is where all the behavior lives.

Docker Compose Files

Docker Compose uses YAML to define multi-container applications. The top-level keys are services, volumes, and networks.

version: "3.9"
services:
  api:
    image: node:18-alpine
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgres://db:5432/app
    depends_on:
      - db

  db:
    image: postgres:15
    volumes:
      - db_data:/var/lib/postgresql/data

volumes:
  db_data:

depends_on controls startup order. volumes maps host paths or named volumes into containers. The YAML Formatter can clean up Compose files that have accumulated inconsistent spacing across edits.

GitHub Actions Workflows

GitHub Actions workflows live in .github/workflows/ and trigger on GitHub events. The continuous integration pipeline below runs on every push to main.

name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up Node
        uses: actions/setup-node@v4
        with:
          node-version: 18
      - name: Install
        run: npm ci
      - name: Test
        run: npm test

on defines triggers. jobs contains one or more jobs. Each job has steps, which are either uses (a pre-built action) or run (a shell command). The workflow YAML maps directly to the build pipeline stages most teams already use.

Ansible Playbooks

Ansible playbooks are YAML files that define what should happen on remote hosts. The format uses a sequence of plays, each targeting specific hosts and running a list of tasks.

---
- name: Deploy web application
  hosts: webservers
  become: yes
  vars:
    app_version: "2.1.0"
  tasks:
    - name: Install Node.js
      package:
        name: nodejs
        state: present

    - name: Copy application files
      copy:
        src: ./app/
        dest: /var/www/app/

    - name: Start application service
      service:
        name: myapp
        state: started
        enabled: yes

hosts targets an inventory group. become: yes runs tasks with elevated permissions. vars sets playbook-level variables. Each task in the tasks sequence calls an Ansible module by name.

The indentation here is load-bearing. A task indented one level too far becomes a subtask of the task above it, not a sibling. Most Ansible debugging sessions eventually come back to a nesting problem.


If you need to convert between formats, the YAML to JSON Converter and JSON to YAML Converter handle both directions cleanly. For related reference material, the Docker cheat sheet and Kubernetes cheat sheet cover the commands that work alongside these YAML configs.

FAQ on YAML

What does YAML stand for?

YAML stands for "YAML Ain't Markup Language." The name is recursive. It was originally called "Yet Another Markup Language," but the creators renamed it to emphasize that YAML is a data serialization format, not a document markup system like HTML or XML.

What is the difference between YAML and JSON?

YAML supports comments, multiline strings, and anchors. JSON does not. YAML is also a superset of JSON, meaning valid JSON is valid YAML. For configuration files, YAML wins on readability. For API responses and data transfer, JSON is faster to parse.

Why does YAML use spaces instead of tabs?

The YAML specification prohibits tabs entirely. Parsers treat tab characters as illegal, throwing a ScannerError. Spaces create consistent, predictable indentation across all editors and environments. Nearly 40% of YAML errors come from indentation mistakes, most involving tabs.

What is a YAML anchor?

An anchor (&name) marks a reusable block of YAML. An alias (*name) references it elsewhere in the same file. This removes repetition in config files. The << merge key lets a mapping inherit anchor values while still overriding specific keys.

How do you write a multiline string in YAML?

Use | (literal block scalar) to preserve newlines exactly. Use > (folded block scalar) to fold line breaks into spaces. Add - after either symbol to strip trailing newlines. Multiline strings are common in Kubernetes pod specs and Ansible task definitions.

What are the YAML data types?

YAML supports six native scalar types: strings, integers, floats, booleans, null, and dates. The parser detects types automatically based on value format. Quote values like "true" or "2024-01-15" to prevent type coercion from converting them into booleans or date objects.

What is the difference between .yml and .yaml?

Nothing functional. Both extensions refer to the same format and are fully interchangeable. Most tools accept either. The YAML specification does not prefer one over the other. Docker Compose defaults to docker-compose.yml. GitHub Actions expects files under .github/workflows/ with either extension.

How do you validate a YAML file?

Use yamllint from the command line for syntax and style checks. The Red Hat YAML extension for VS Code validates in real time as you type. Online tools like the YAML Validator catch structural errors without any local setup required.

What is YAML used for in Kubernetes?

Every Kubernetes resource is defined in a YAML manifest. The file specifies apiVersion, kind, metadata, and spec. Kubernetes reads the desired state from the manifest and reconciles the cluster to match it. Production Kubernetes deployments reached 80% of organizations in 2024, per CNCF data.

How do you convert YAML to JSON?

Any valid YAML can be converted to JSON since YAML is a superset of JSON. Command-line tools like yq handle conversion directly. The YAML to JSON Converter works in the browser. The reverse is also possible using the JSON to YAML Converter.