YAML Formatter and Validator

Re-indent YAML, find the line that breaks your deploy, and see where YAML 1.1 and 1.2 disagree about what a value means.

Input

Settings

History

Load from URL

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 compact containers:\n- name: web style that kubectl output 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.

Input
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}
Output
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
Open this example in the tool

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”.

Input
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
Output
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
Open this example in the tool

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.

Input
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]
Output
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 ]
Open this example in the tool

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.

Input
ntp_enabled: yes
firewall: off
allowed_countries: [SE, DK, NO, FI]
admin_users:
  - name: deploy
    sudo: true
Output
ntp_enabled: yes
firewall: off
allowed_countries: [ SE, DK, NO, FI ]
admin_users:
  - name: deploy
    sudo: true
Open this example in the tool

Common errors and how to fix them

ErrorCauseFix
Tabs are not allowed for indentation in YAML
Explained
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 key
Explained
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 column
Explained
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 unique
Explained
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): baseAn 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.

Related tools