You copy a block out of a Kubernetes manifest, paste it into a script that expects JSON, and get a parse error. Fair enough: YAML and JSON hold the same data, but they spell it very differently.
This guide shows how the two map onto each other, the indentation rules that catch everyone at least once, and what quietly changes when you convert.
What YAML is
YAML (YAML Ain’t Markup Language) is a data format designed to be easy for people to read and write. Instead of braces and brackets, it uses indentation and line breaks to show structure, so a YAML file reads like an outline.
It's the usual configuration format for Kubernetes, Docker Compose, GitHub Actions, GitLab CI, Ansible and Helm. Many APIs and libraries expect JSON instead, so you’ll find yourself converting between the two more often than you’d think.
YAML and JSON side by side
Both formats describe the same kinds of data: objects (mappings in YAML), arrays (sequences) and scalars, which are single values such as strings, numbers, booleans and null. Here is the same configuration in each:
# Application configuration
app:
name: PulpMiner
version: 2.4.1
debug: false
server:
host: 0.0.0.0
port: 8080
database:
host: db.example.com
port: 5432
credentials:
username: admin
password: secret123
features:
- name: scraping
enabled: true
- name: scheduling
enabled: false{
"app": {
"name": "PulpMiner",
"version": "2.4.1",
"debug": false
},
"server": {
"host": "0.0.0.0",
"port": 8080
},
"database": {
"host": "db.example.com",
"port": 5432,
"credentials": {
"username": "admin",
"password": "secret123"
}
},
"features": [
{ "name": "scraping", "enabled": true },
{ "name": "scheduling", "enabled": false }
]
}The YAML version is shorter, allows comments (lines starting with #) and doesn't need quotes around most strings. Types are inferred: 8080 is a number, false is a boolean, and 2.4.1 is a string because it isn't a valid number.
YAML indentation rules
Indentation decides the structure, so getting it wrong either changes your data or causes a parse error. YAML may be the only format where two stray spaces can take down a deploy.
- Use spaces, not tabs. YAML doesn't allow tabs for indentation.
- Keep siblings aligned. Keys at the same level must start in the same column. Any width works, but two spaces per level is the convention.
- Indent children further than their parent. A key at the same column as the one above it is a sibling, not a child.
- Start list items with a dash and a space (
-), all at the same indentation.
# Correct: host and port belong to server
server:
host: localhost
port: 3000
# Wrong: host and port are siblings of server, which is null
server:
host: localhost
port: 3000
# Correct: list items under the key
fruits:
- apple
- banana
# Wrong: port is indented further than its sibling
database:
host: localhost
port: 5432Where you meet YAML
Kubernetes
Kubernetes manifests describe the desired state of an application: the container image, replica count, resource limits and more. kubectl accepts the same manifest as JSON, and kubectl get ... -o json prints resources as JSON.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 3
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: web-app
image: myregistry/web-app:1.2.0
ports:
- containerPort: 8080
resources:
limits:
memory: "256Mi"
cpu: "500m"Docker Compose
Compose files define multi-container applications. Run docker compose config --format json to see the fully resolved file as JSON.
services:
web:
build: ./frontend
ports:
- "3000:3000"
depends_on:
- db
db:
image: postgres:16
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_DB: myapp
POSTGRES_PASSWORD: secret
volumes:
pgdata:GitHub Actions
Workflow files are YAML. Converting them to JSON is handy when you want to generate or check them with a script.
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- run: npm ci
- run: npm testConverting YAML to JSON in code
JavaScript (Node.js)
Using the yaml package (npm install yaml):
import { readFileSync, writeFileSync } from "node:fs";
import { parse } from "yaml";
const data = parse(readFileSync("config.yaml", "utf-8"));
writeFileSync("config.json", JSON.stringify(data, null, 2));Python
Using PyYAML (pip install pyyaml). Always use safe_load, which doesn't construct arbitrary Python objects.
import json
import yaml
with open("config.yaml") as f:
data = yaml.safe_load(f)
with open("config.json", "w") as f:
# default=str turns dates into strings instead of raising TypeError
json.dump(data, f, indent=2, default=str)Command line with yq
yq converts in both directions:
brew install yq
# YAML to JSON
yq -o=json config.yaml > config.json
# JSON to YAML
yq -p=json -o=yaml config.json > config.yaml
# From stdin
cat config.yaml | yq -o=jsonWhat changes in the conversion
Some YAML features have no JSON equivalent:
- Comments are dropped. JSON has no comment syntax.
- Anchors and aliases are expanded. YAML lets you define a value once with
&nameand reuse it with*name. In JSON the value is simply repeated. - Multi-line strings become escaped strings. Literal (
|) blocks keep their line breaks as\n. Folded (>) blocks join lines with spaces. - Types are inferred, and parsers disagree. YAML 1.1 parsers such as PyYAML read
yes,no,onandoffas booleans. YAML 1.2 parsers, including the JavaScriptyamlpackage, keep them as strings. Quote any value you want to stay a string.
enabled: yes # true in YAML 1.1, "yes" in YAML 1.2
empty: null # null (so are ~ and an empty value)
version: 1.0 # the number 1.0, written as 1 in JSON
octal: 0o777 # 511 in YAML 1.2
date: 2026-02-10 # PyYAML returns a date object
# Quote values to keep them as strings
enabled: "yes"
version: "1.0"
date: "2026-02-10"Using the YAML and JSON converter
- Open the YAML and JSON converter and pick YAML to JSON or JSON to YAML.
- Paste your input and select Convert. If the input can't be parsed, the error message is shown instead of output.
- Copy the result, or select Swap to move the output into the input and convert it back the other way.
The converter runs in your browser, so nothing you paste leaves your machine. It handles nested mappings and lists, quoted strings, inline [a, b] and {a: b} values, | and > blocks and comments. Like YAML 1.1, it reads yes and no as booleans. It doesn't support anchors, aliases or files with several documents; use yq for those.
