YAML Diff / Compare
Compare two YAML documents structurally — added, removed, changed and retyped keys, order-insensitive by default, entirely on this device.
Drop a YAML file into either side — it is read on this device and never uploaded.
Press ? for shortcuts
Do it offline:$ diff <(yq -P 'sort_keys(..)' a.yaml) <(yq -P 'sort_keys(..)' b.yaml)
How to use it
- Paste the older revision into side A and the newer one into side B, or drop a file onto either side — a dropped file never leaves the tab.
- Press Compare. The strip above the result counts added, removed, changed and retyped keys, and every row names the path that moved.
- Turn on Key order matters when the written order of a mapping is part of your review, or Ignore list order when a re-sorted list is not an edit.
- Step through with next and previous, jump to the line in either pane, then copy the report, download it, or share a link.
Two revisions of the same config, and the only useful question is which keys moved. Both sides are read as YAML and answered in keys and values: what arrived, what left, what holds a different value, and what quietly took a different type. Re-indenting and requoting are not differences here. Neither side leaves this tab.
Examples
name: web
image: nginx:1.25
replicas: 2
ports:
- 80
- 443
env:
LOG_LEVEL: info
REGION: eu
YAML structural comparison
1 added, 1 removed, 3 changed, 0 retyped
~ doc[0]./replicas — Changed: 2 → 3
~ doc[0]./image — Changed: nginx:1.25 → nginx:1.26
+ doc[0]./ports/2 — Added: 8080
− doc[0]./env/REGION — Removed: eu
~ doc[0]./env/LOG_LEVEL — Changed: info → debug
Side B was rewritten top to bottom, yet only five keys moved. Mapping segments of a path are keys and list segments are positions, so the new port is reported at position 2.
defaults: &defaults
retries: 3
timeout: 30
prod:
<<: *defaults
timeout: 60
YAML structural comparison
0 added, 0 removed, 0 changed, 0 retyped
No differences found
One side leans on an anchor and a merge key, the other spells everything out. Both describe the same two mappings, so the verdict is no difference, with notes saying an alias was followed and a merge key resolved.
port: !!str 8080
name: "web"
YAML structural comparison
0 added, 0 removed, 0 changed, 1 retyped
⇄ doc[0]./port — Retyped: !!str 8080 → !!int 8080
The edge case. Both ports read as 8080 in the source, but one is tagged text and the other resolves to an integer, so this counts as retyped rather than changed. The quotes around web are style only.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
Key order is not a difference by default
A mapping is matched by its keys, so moving a block up the file changes nothing. Turn on Key order matters and the written order joins the comparison; a note then reports order as the only difference.
An alias is compared as the value it stands for
Anchors and aliases are followed before matching, so a pointer and the value it points at count as the same node. Merge keys resolve the way a loader resolves them, and renaming an anchor is not reported.
Two scalars, one text, different types
Types come from the resolved tag, not from the characters: a tagged text 8080 and a plain 8080 are retyped, and so are 1 and 1.0, which resolve to an integer and a float. Retyped is counted apart from changed.
Comments never take part
A comment carries no node, so an edit that only touches comments reads as no difference, with a note saying they were ignored. Blank lines, indentation width, flow or block style and line folding are read the same way.
Documents line up by position
A stream split by three dashes is compared document one against document one. Extra documents on either side are whole additions or removals, and a note reports the two counts whenever they disagree.
Where the comparison stops
Each side is capped at 20 MB, the first 5,000 differences are listed while the summary keeps counting, and alias expansion stops after 400 dereferences, which makes a bomb refuse instead of hang. Past roughly 1,000 levels of nesting it declines; a share link stops at 8 KB compressed.
Common errors
Tab used as indentation at line 2, column 1 — YAML forbids tabs in indentation; replace the tab with spaces.- A pane that cannot be parsed has no structure to compare, so that side stops and the other keeps its text. The position is where the tab sits.
Missing space after the colon at line 1, column 5 — `name:web` reads as one scalar, not a key/value pair; write `name: web`.- Written that way the line is one long value rather than a key, so a plausible-looking comparison would have been wrong. The wording is the parser's, unchanged.
duplicate key `port` at lines 1 and 3 — the last one wins, so only it is compared- A warning, not a refusal. The repeated key is loaded the way a parser loads it, so the earlier value never reaches the comparison. Twenty are listed per side.
Alias expansion exceeded the 400-alias budget for this tool — this pattern matches an alias-expansion bomb; inline the repeated nodes to compare.- Aliases are followed before matching, and a few nested layers of them describe more nodes than a machine could hold. The budget counts dereferences, so this arrives in under a second.
Side A input is larger than the 20 MB limit for this tool. For bigger files use the CLI: diff <(yq -P 'sort_keys(..)' a.yaml) <(yq -P 'sort_keys(..)' b.yaml)- A ceiling of the page itself, tested before a character is parsed and quoting the command-line equivalent. It counts per side.
FAQ
Why do two files whose keys sit in a different order come out identical?
Because a mapping is a set of key/value pairs, and a loader hands your program the same object either way. Matching by key is what keeps a reformatted config readable here: keys someone sorted alphabetically stay quiet, and the one value that really moved stands alone. Key order matters puts order back in.
Is an anchor compared by name, or by the value it points to?
By the value. An alias is followed to its anchor before matching, so a side that factors shared settings into an anchor and a side that repeats them agree. Merge keys resolve the same way. The trade is that anchor names are invisible: rename one and nothing is reported.
Why is 8080 not the same as "8080" on this page?
They resolve to different tags — an integer and a piece of text — and a program reading the file receives different things. That is counted as retyped rather than changed, so a port that silently became text shows up in the summary. Quote style alone never counts: text stays text bare, single- or double-quoted.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Compare now, or re-run.
- s
- Swap sides A and B.
- n
- Go to the next difference.
- p
- Go back one difference.
- ⌘/Ctrl + C
- Copy the report, nothing selected; Shift forces it.