The basics
What is a YAML file?
A YAML file stores data as text that reads almost like a set of notes: a key, a colon and a value on each line, with indentation for anything nested and dashes for lists. It holds the same kinds of data as JSON (objects, lists, text, numbers, booleans and null) but adds comments, multi-line text and a way to reuse parts of the file.
It's mostly used for configuration that people edit by hand: Docker Compose files, Kubernetes manifests, CI pipelines such as GitHub Actions and GitLab CI, Ansible playbooks, OpenAPI descriptions, and the front matter at the top of Markdown pages in static site generators.
Three things YAML is not:
- Not markup. It has no tags around text, and its name says so: YAML Ain't Markup Language.
- Not forgiving about whitespace. Indentation is the structure. One space too many or too few changes the data or stops the file from loading.
- Not a schema. A YAML file doesn't say which keys are required or what they mean. Each tool that reads it decides that, and editors can check it with a JSON Schema.
Anatomy
What's inside a YAML file
YAML has three building blocks: mappings (key: value pairs), sequences (lists) and scalars (single values such as text or numbers). Indentation shows what belongs inside what. Here is a small but complete file, part by part.
# Settings for a small web app
name: Sample app
version: "2.10"
debug: false
port: 8080
owner: ~
database:
host: db.example.internal
ports: [5432, 5433]
servers:
- name: web-1
region: eu-west
- name: web-2
region: us-east
defaults: &defaults
retries: 3
worker:
<<: *defaults
timeout: 30
motd: |
Line one
Line two- 1
# Settings for a small web appA comment
Everything from
#to the end of the line is ignored. The#must be at the start of a line or after a space:color: #fffis a comment too, socolorends up empty. - 2
name: Sample appA key and a value
A key, a colon, a space, then the value. The space after the colon is required. Text needs no quotes unless it could be read as something else.
- 3
version: "2.10"A quoted string
Unquoted,
2.10would load as the number 2.1. Quotes keep version numbers, postcodes and IDs with leading zeros as text. - 4
debug: falseA boolean
trueorfalse. In the older YAML 1.1 rules,yes,no,onandoffare booleans as well; see the Norway problem below. - 5
port: 8080A number
Plain digits load as an integer,
0.75as a float. YAML 1.2 also reads0o14as octal and0x1Fas hexadecimal. - 6
owner: ~Null
~,nullor nothing at all after the colon all mean an empty value. - 7
database: host: …A nested mapping
Indented lines belong to the key above them. Use spaces only: YAML forbids tabs in indentation. Two spaces per level is the usual style, but any consistent number works.
- 8
ports: [5432, 5433]A flow sequence
A list in square brackets, as in JSON. Mappings can be written in braces the same way:
{host: db, port: 5432}. - 9
- name: web-1 region: eu-westA block sequence of mappings
Each
-starts a list item. Here each item is itself a mapping, the usual shape for lists of servers, users or build steps. - 10
&defaults … <<: *defaultsAn anchor, an alias and a merge key
&defaultsnames a node and*defaultsrefers back to it. The<<merge key copies its keys intoworker, which then adds its own. Merge keys come from YAML 1.1; most parsers still support them. - 11
motd: |A block scalar
|keeps the line breaks of the indented text below it;>folds them into spaces. Useful for messages, scripts and certificates.
Under the hood
Quoting and types: how YAML decides what a value is
An unquoted value is a plain scalar, and the parser guesses its type from what it looks like: 42 becomes a number, true a boolean, null empty, everything else text. That guessing makes YAML pleasant to write and is also the source of most of its surprises. Quoting a value always makes it a string.
| Written as | Loads as (YAML 1.2) | Note |
|---|---|---|
port: 8080 | Integer 8080 | |
version: 3.10 | Float 3.1 | The trailing zero is lost. Write "3.10". |
zip: 01234 | Integer 1234 | Leading zero dropped. |
released: 2026-10-27 | Text in the core schema; a date in PyYAML, js-yaml and ruamel.yaml | Many loaders still add the YAML 1.1 timestamp type. |
path: 'C:\Users\ada' | Text, backslashes kept | In single quotes nothing is escaped; write '' for a quote. |
msg: "a\tb" | Text with a tab | Double quotes allow escapes such as \t, \n and \u00e9. |
title: Note: read me | Error | A second colon and space starts another mapping. Quote the value. |
color: #fff | Null | # after a space starts a comment. Quote it. |
key: @admin | Error | @ and the backtick are reserved and can't start a plain value. |
When in doubt, quote it
Values that start with *, &, !, %, @, {, [, |, >, # or a quote, values that contain : or #, and anything that looks like a number, date or boolean but should stay text: put them in quotes.
Versions
The Norway problem: YAML 1.1 vs YAML 1.2
YAML 1.1 (2005) recognised many more booleans and number formats than you'd expect. A list of country codes containing NO, for Norway, loads as false. YAML 1.2 (2009) cut the implicit types down to a small core schema, mainly to make YAML a superset of JSON, but many popular parsers still follow the 1.1 rules. The result: the same file can produce different data in different tools.
| Written as | YAML 1.1 (PyYAML) | YAML 1.2 (js-yaml, ruamel.yaml) |
|---|---|---|
NO | false | "NO" |
yes, no, on, off | true, false | Text |
On (as in light: On) | true | "On" |
0755 | 493 (octal) | 755 |
0o755 | "0o755" (text) | 493 |
012345 | 5349 (octal) | 12345 |
1:30 | 90 (base 60) | "1:30" |
1e3 | "1e3" (text) | 1000 |
on: as a key | The boolean true | "on" |
The YAML 1.1 boolean type also lists y and n, but PyYAML doesn't apply that rule, so it reads them as text. The last row matters for CI files: a GitHub Actions workflow starts with on:, which PyYAML loads as the key True. Try it with sample-workflow.yml.
How to avoid it
Quote values that must stay text, especially country codes, versions, postcodes, times and IDs. If you choose the parser, prefer one that follows YAML 1.2. A %YAML 1.2 line at the top documents the intent, though not every parser changes its behaviour because of it.
Features
Anchors, aliases and multiple documents
Anchors and aliases avoid repetition. &name marks a value and *name reuses it later in the same file. With the << merge key, a mapping can copy another mapping's keys and override some of them, which is how Docker Compose and CI files share settings between services or jobs.
base: &base
timeout: 30
retries: 3
service:
<<: *base
timeout: 5 # overrides the merged valueWhen a loader reads this, service simply contains timeout: 5 and retries: 3. The anchor itself disappears: converting the file to JSON writes the values out in full.
Multiple documents can share one file, separated by a line with ---. A line with ... marks the end of a document. Kubernetes uses this to put several resources in one manifest. Most libraries have a separate function for it, such as yaml.safe_load_all in Python or loadAll in js-yaml; the single-document function raises an error when it finds a second document.
Comparison
YAML vs JSON, TOML and XML
| YAML | JSON | TOML | XML | |
|---|---|---|---|---|
| Structure from | Indentation | Brackets and braces | [sections] and key = value | Tags |
| Comments | Yes, # | No | Yes, # | Yes, <!-- --> |
| Implicit types | Many, version-dependent | Few and strict | Few and strict | None: everything is text |
| Reuse within a file | Anchors and aliases | No | No | Entities |
| Several documents per file | Yes, --- | No (JSON Lines is one per line) | No | No |
| Typical use | Config people edit: CI, Kubernetes, Compose | APIs, data exchange, generated config | App config: pyproject.toml, Cargo.toml | Documents, older enterprise formats |
YAML 1.2 was designed so that a JSON document is also valid YAML: a YAML parser reads {"a": [1, 2]} as it is. The reverse isn't true, and converting YAML to JSON drops comments and writes anchors out in full.
Troubleshooting
Why won't my YAML load?
- A tab in the indentation. PyYAML reports
found character '\t' that cannot start any token. Set your editor to insert spaces in YAML files. - Inconsistent indentation. Sibling keys must start in the same column. Errors such as
while parsing a block mappingorbad indentation of a mapping entryusually point at the line after the real mistake. mapping values are not allowed here. A value contains a colon followed by a space, as intitle: Note: read me. Quote the value.found undefined alias. A value starts with*but isn't an alias. Quote it, or define the anchor before it is used.- Template braces.
name: {{ project }}from a templating tool is read as a flow mapping and fails. Quote it:name: "{{ project }}". - Duplicate keys. The spec says keys must be unique, but parsers differ: PyYAML silently keeps the last value, js-yaml stops with
duplicated mapping key. - The data is wrong but there's no error. Check the Norway problem table above: an unquoted
NO,3.10or0755is the usual cause.
To find problems before a tool does, run yamllint file.yaml, which checks syntax, indentation, duplicate keys and style, or open the file in VS Code with a YAML extension that validates against the tool's JSON Schema.
How to
How to open, edit and convert a YAML file
- Open and edit it: any text editor works. A code editor such as VS Code, with a YAML extension, adds syntax highlighting, indentation guides and validation. Avoid word processors, which change quotes and may insert tabs.
- View it in a browser: browsers show a YAML file as plain text when it's served as text; some download it instead, depending on the type the server sends.
- Convert to JSON:
yq -o=json file.yamlwith the Go version of yq, or in Pythonjson.dumps(yaml.safe_load(open('file.yaml'))). Comments are lost. - Convert JSON to YAML:
yq -p json -o yaml file.json. Because YAML 1.2 accepts JSON, you can also rename a.jsonfile to.yamland it still loads. - Query or change values from the command line:
yq '.database.host' config.yamlreads one value;yq -i '.port = 9090' config.yamledits it in place. - .yaml or .yml: the content is identical. RFC 9512 prefers
.yaml; some tools look for one spelling only, such asdocker-compose.ymlor.github/workflows/*.yml, so match what the tool documents.
Security
Is YAML safe? Unsafe loading and alias bombs
YAML tags such as !!python/object/apply ask the parser to build objects of a given type. A loader that honours them for any type can be made to run code just by reading a file. This has caused real vulnerabilities:
- PyYAML before 5.1 ran such tags through the default
yaml.load()(CVE-2017-18342). Since 6.0,yaml.load()requires an explicitLoader. Useyaml.safe_load()for anything from outside, and neveryaml.unsafe_load(). - SnakeYAML (Java) let its default
Constructorbuild any class on the classpath until version 2.0, released in 2023 (CVE-2022-1471). Upgrade, or useSafeConstructoron older versions. - Ruby's Psych made
YAML.loadsafe by default in version 4, which ships with Ruby 3.1.
Even a safe loader can be overwhelmed. Aliases that point to sequences of aliases, nine levels deep, expand a file of a few hundred bytes into hundreds of millions of values: the YAML version of the XML "billion laughs" attack. RFC 9512 recommends limiting alias expansion and validating untrusted input. Set size and alias limits where your library allows them; some, such as SnakeYAML, cap the number of aliases by default.
Configuration files often end up holding passwords and API keys. Keep secrets out of YAML that is committed to a repository or shared as a link; use environment variables or a secrets manager, and reference them from the file.
Free downloads
Sample YAML files to download
Free sample YAML files for testing parsers, config loaders, CI tools and upload limits. Each one loads without errors in PyYAML, ruamel.yaml, js-yaml and the yaml package for JavaScript.
Loading…What's inside sample-config.yaml
The file is commented: each numbered comment marks one of these parts.
Comments 1 and 15: document markers
---starts the document and...ends it. Both are optional in a single-document file, but some tools write them.Comments 2 to 7: scalars
Plain text, a quoted version number, a boolean, integers, a float, a null and a date that most loaders turn into a date object.
Comment 8: nesting
A
databasemapping with anoptionsmapping inside it, indented with two spaces per level.Comments 9 to 11: lists
A block sequence, a flow sequence and a flow mapping in JSON-like brackets, and a list of mappings.
Comment 12: anchor, alias and merge
workermerges the keys ofdefaultsand overrideslog_level, so it loads asretries: 3, log_level: debug.Comment 13: multi-line text
A literal block (
|) that keeps its line breaks and a folded block (>) that joins its lines with spaces.Comment 14: quoting
A quoted
"NO", a Windows path in single quotes, escapes in double quotes, a value with a colon, and text in German, Japanese and Arabic with an emoji.
All sample YAML files
Commented config file
sample-config.yaml · 2.0 KB (2,041 bytes)
An app configuration with 15 numbered comments covering scalars, nesting, both list styles, an anchor with a merge key, block scalars, quoting and Unicode.
- Teaching the parts of a YAML file
- Testing config loaders and syntax highlighting
YAML 1.1 vs 1.2 differences
sample-norway-problem.yaml · 549 bytes
Values that load differently under the 1.1 and 1.2 rules:
NO,yes/no/on/off,y/n,3.10,0755,0o755,012345,1:30,1e3,.nanand-.inf.- Finding out which rules your parser follows
- Comparing parsers and languages
Anchors, aliases and merge keys
sample-anchors.yaml · 783 bytes
A reused scalar, mapping and sequence, a merge with an override, and a merge of two mappings where the first one wins.
- Testing alias and merge-key support
- Checking YAML-to-JSON conversion
Three documents in one file
sample-multi-document.yaml · 896 bytes
A ConfigMap, a Deployment and a Service in the style of a Kubernetes manifest, separated by
---and ended with.... All names are fictional.- Testing multi-document loading
- Checking tools that split or merge manifests
CI workflow (.yml)
sample-workflow.yml · 687 bytes
A test-and-build workflow in the style GitHub Actions uses, with the
.ymlextension, a matrix,${{ }}expressions and anon:key that YAML 1.1 loaders turn intotrue.- Testing `.yml` uploads and content-type handling
- Showing the `on:` key problem
1 MB YAML file
sample-1mb.yaml · 1 MB (1,048,576 bytes)
A list of 8,783 fictional records, padded with a final comment to exactly 1,048,576 bytes (1 MiB).
- Upload and request-size limits
- Parser performance
Ways to test with them
- Which rules your parser follows: load
sample-norway-problem.yamland look atcountries.["GB", false, "SE"]means YAML 1.1 rules;["GB", "NO", "SE"]means YAML 1.2. - Multi-document support: a single-document loader should report an error on
sample-multi-document.yaml; a multi-document loader should return three documents. - Merge keys:
serviceinsample-anchors.yamlshould load astimeout: 5, retries: 3, name: search. - Upload limits and
.yml:sample-1mb.yamlis exactly 1,048,576 bytes, andsample-workflow.ymlchecks that both extensions are accepted and served as text.
Free to use for testing, teaching and demos, commercially or not, with no attribution needed. A link back to this page is always appreciated.
Put it online
Have a YAML file? Give it a link.
Drop a YAML file on 99helpers and it gets its own web address, for a config example, an OpenAPI description or a test fixture. Visitors can read it in the browser or download it, and scripts can fetch it from the link.
Questions
YAML files, frequently asked
What does YAML stand for?
YAML Ain't Markup Language, a recursive name that stresses it is for data, not for marking up documents.
What's the difference between .yaml and .yml?
None in the content. RFC 9512 prefers .yaml, and .yml is common too. Some tools only look for one spelling, so use whichever the tool's documentation shows.
How do I open a YAML file?
With any text editor, such as Notepad, TextEdit in plain-text mode or VS Code. A code editor with a YAML extension highlights the structure and flags indentation errors.
Can YAML use tabs?
Not for indentation: the spec forbids it, and parsers stop with an error. Tabs are allowed inside quoted text. Configure your editor to insert spaces in YAML files.
What is the Norway problem?
Under the YAML 1.1 rules, the unquoted country code NO loads as the boolean false, along with yes, no, on and off. YAML 1.2 fixed it, but many parsers still use 1.1 rules, so quote such values.
Is JSON valid YAML?
Yes, under YAML 1.2, which was designed as a superset of JSON. A YAML 1.2 parser reads a JSON file and returns the same data.
How do I convert YAML to JSON?
Use yq -o=json file.yaml, or load it with a YAML library and write it out with a JSON library. Comments disappear and anchors are written out in full.
Is it safe to load a YAML file?
With a safe loader, yes. Unsafe loaders can build arbitrary objects and run code from a crafted file, so use functions like Python's yaml.safe_load(), and limit size and aliases for untrusted input.
Related formats
Other file types explained
Keep going
Related
Sources
- YAML Ain't Markup Language, version 1.2, revision 1.2.2
- RFC 9512: YAML Media Type
- Boolean type for YAML 1.1 (yaml.org)
- Red Hat: CVE-2017-18342 (PyYAML yaml.load)
- Veracode: resolving CVE-2022-1471 with SnakeYAML 2.0
Last reviewed 27 October 2026.