Where you meet YAML
YAML is the configuration language of Kubernetes manifests, Helm values, GitHub Actions and GitLab CI pipelines, Docker Compose files, Ansible playbooks and OpenAPI specs. It is meant to be easy to read, but its reliance on indentation means one misplaced space can turn a list item into a map key, and the resulting error often points somewhere other than the real mistake.
This formatter parses the whole document with the yaml library (YAML 1.2 by default, 1.1 on request), reports every error with a line number and a hint, and re-prints the file with consistent indentation. Comments, blank-line grouping, anchors (&base), aliases (*base), merge keys (<<), tags and multi-document streams separated by --- survive the round trip.
Quick start
Paste a manifest or drop a .yaml / .yml file. Format with Ctrl/Cmd+Enter, copy with Ctrl/Cmd+Shift+C, and use Ctrl/Cmd+K for the command palette (switch to the Tree or Table view, convert to JSON, query with JSONPath). Ctrl/Cmd+Shift+M produces a one-line flow-style version with comments removed, handy for embedding YAML in an environment variable.
Secrets in Helm values or CI files are a real concern with online tools. Here parsing happens inside your browser and no request carries your text.
Formatting options
- Collections: As written keeps each map and list in the style it already has. Block (indented) expands inline
{ }and[ ]collections into indented lines, which is what most style guides want for Kubernetes. Flow ({ } and [ ]) does the reverse and wraps at the line width. - String quotes: As written leaves quoting alone. Plain where possible removes quotes that are not needed; it keeps them where removing would change the type, so
"8080"stays a string. That judgement follows the selected YAML version: under 1.2,"NO"loses its quotes, so pick 1.1 if a 1.1 parser will read the file. Double quotes and Single quotes quote every string value. Block scalars (|and>) are never touched, and keys stay plain. - Indent lists under keys: on gives
containers:\n - name: web; off gives the compactcontainers:\n- name: webstyle thatkubectloutput uses. - YAML version: see the next section.
Indent can be 2 or 4 spaces; if you choose tabs, the output uses 2 spaces and says why, because the YAML spec forbids tab indentation. Sort keys orders map keys alphabetically, which is useful before diffing two exported manifests.
YAML 1.1, YAML 1.2 and the Norway problem
In YAML 1.1, the plain words yes, no, on, off, y and n are booleans. YAML 1.2 dropped that rule, so they are ordinary strings. Many widely used libraries still follow 1.1, including PyYAML (and therefore Ansible) and the go-yaml v2 library behind older Kubernetes tooling. The classic failure is a list of country codes where NO (Norway) silently becomes false.
With YAML version set to YAML 1.2, the formatter adds an info note on every such value. Switch to YAML 1.1 to see how a 1.1 parser will read the file; those notes become warnings. Quoting the value ("NO") is correct under both versions.
Anchors, aliases and alias bombs
Formatting never expands aliases; *defaults stays *defaults. The Tree view and conversions such as YAML to JSON do have to expand them, and a malicious file can nest aliases so that a few lines expand to billions of nodes (the YAML version of “billion laughs”). Expansion stops at 1,000 aliases with a clear message instead of freezing the tab.
Examples
Kubernetes Deployment with mixed indentation
Four-space and two-space blocks are normalised and the inline label and limit maps are expanded into indented lines.
apiVersion: apps/v1
kind: Deployment
metadata:
name: checkout
labels: {app: checkout, tier: backend}
spec:
replicas: 3
selector:
matchLabels: {app: checkout}
template:
metadata:
labels: {app: checkout}
spec:
containers:
- name: api
image: "registry.example.com/checkout:2.4.0"
ports:
- containerPort: 8080
resources:
limits: {cpu: 500m, memory: 512Mi}
apiVersion: apps/v1
kind: Deployment
metadata:
name: checkout
labels:
app: checkout
tier: backend
spec:
replicas: 3
selector:
matchLabels:
app: checkout
template:
metadata:
labels:
app: checkout
spec:
containers:
- name: api
image: "registry.example.com/checkout:2.4.0"
ports:
- containerPort: 8080
resources:
limits:
cpu: 500m
memory: 512Mi
GitHub Actions workflow
The on: key is reported because a YAML 1.1 parser reads it as the boolean true; GitHub’s own parser treats it as the string “on”.
name: CI
on:
push: {branches: [main]}
pull_request:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix: {node: [20, 22, 24]}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: {node-version: "${{ matrix.node }}", cache: npm}
- run: npm ci
- run: npm test -- --coverage
name: CI
on:
push: { branches: [ main ] }
pull_request:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix: { node: [ 20, 22, 24 ] }
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: "${{ matrix.node }}", cache: npm }
- run: npm ci
- run: npm test -- --coverage
Docker Compose file with anchors and merge keys
The &defaults anchor and both <<: *defaults merges are printed exactly as written while the rest is re-indented.
x-service-defaults: &defaults
restart: unless-stopped
logging: {driver: json-file, options: {max-size: "10m"}}
services:
api:
<<: *defaults
image: shop/api:1.8
environment: [DATABASE_URL=postgres://db:5432/shop, LOG_LEVEL=info]
worker:
<<: *defaults
image: shop/worker:1.8
depends_on: [api]
x-service-defaults: &defaults
restart: unless-stopped
logging: { driver: json-file, options: { max-size: "10m" } }
services:
api:
<<: *defaults
image: shop/api:1.8
environment: [ DATABASE_URL=postgres://db:5432/shop, LOG_LEVEL=info ]
worker:
<<: *defaults
image: shop/worker:1.8
depends_on: [ api ]
Ansible variables read as YAML 1.1
Under YAML 1.1 the formatter warns that yes, off and NO are booleans, which is how Ansible will actually read them.
ntp_enabled: yes
firewall: off
allowed_countries: [SE, DK, NO, FI]
admin_users:
- name: deploy
sudo: true
ntp_enabled: yes
firewall: off
allowed_countries: [ SE, DK, NO, FI ]
admin_users:
- name: deploy
sudo: true
Common errors and how to fix them
| Error | Cause | Fix |
|---|---|---|
Tabs are not allowed for indentation in YAMLExplained | A line is indented with a tab character, usually from an editor that inserts tabs or a snippet pasted from a terminal. | Replace the tab with spaces. Formatting with the default indent rewrites the whole file with spaces once the offending line is fixed. |
A nested block cannot be used as a keyExplained | A value contains ": " without quotes (for example title: Note: read me), or a line is indented under a scalar. PyYAML reports this as “mapping values are not allowed here”. | Quote the value, or fix the indentation of the line below so it lines up with its siblings. |
All mapping items must start at the same columnExplained | Keys of the same map are indented by different amounts, often after copying a block from another file. | Align the key with the other keys of its map; the error line is the one that is out of step. |
Duplicate key — keys in a mapping must be uniqueExplained | The same key appears twice in one map. Some parsers keep the last value silently, which hides real configuration bugs. | Delete or merge one of the two entries. |
Plain value cannot start with reserved character @Explained | Unquoted values cannot begin with @ or a backtick; npm scopes like @acme/ui trigger this. | Wrap the value in quotes: “@acme/ui”. |
Unresolved alias (the anchor must be set before the alias): base | An alias *base refers to an anchor that is defined later in the file, misspelt, or lives in another document after —. | Move the &base anchor above its first use and check the spelling. |
Frequently asked questions
Why does my YAML say "found character that cannot start any token"?
That is PyYAML’s message for a tab used as indentation or a value starting with a reserved character such as @ or a backtick. Paste the file here and the exact line is highlighted with a plain-English explanation.
Does formatting keep my comments?
Yes, in normal formatting comments and blank lines between groups are preserved. Minify removes them because a single-line flow document has no place for comments.
How do I validate a Kubernetes YAML file?
Paste it here to check YAML syntax and indentation. This catches parse errors but not schema problems such as a misspelt field name; for that, run kubectl apply --dry-run=server or kubeconform against your cluster version.
Is YAML a superset of JSON?
YAML 1.2 is, for practical purposes: any JSON document is valid YAML 1.2 and parses to the same data. YAML 1.1 has small differences, mainly around booleans and escapes.
Are my Helm values or secrets sent anywhere?
No. The YAML parser runs in your browser, so passwords and tokens in the file stay on your machine.