yq vs jq: When to Use Each (Two yq Implementations, One jq)
Last updated:
jq is the JSON command-line processor — a C binary with a domain-specific filter language that has been the reference standard for command-line JSON work since 2012. yq is the YAML-focused cousin, except there are two separate tools both named yq: mikefarah/yq is a Go binary with native YAML support and a jq-like filter dialect, and kislyuk/yq is a Python wrapper that shells out to jq with YAML/XML/TOML adapters on top. Picking between them depends on what you are editing (JSON vs YAML), whether you need in-place file edits, and whether you already know jq syntax cold. This guide compares all three side by side, with the same query expressed in each dialect and a decision checklist at the end. For broader jq reference material, see our jq cheat sheet and jq filter examples.
Converted some YAML to JSON and the file refuses to parse? Drop it into Jsonic's JSON Validator — it points at exact line and column for trailing commas, unquoted keys, and bracket mismatches that round-trips through YAML often leave behind.
Validate the JSON outputThe yq confusion: TWO different tools both named yq
The single biggest source of pain when reading yq tutorials is that two unrelated projects share the name. They are not different versions of one tool — they are independent codebases with different authors, languages, install paths, and syntax dialects.
- mikefarah/yq — github.com/mikefarah/yq. Written in Go by Mike Farah. Started in 2017. Native YAML parser, no jq dependency. Current major version 4.x. Distributed as a single static binary (no runtime). Default
yqin Homebrew and most Linux distro packages. - kislyuk/yq — github.com/kislyuk/yq. Written in Python by Andrey Kislyuk. Started in 2016. A wrapper around jq: converts YAML/XML/TOML to JSON, calls jq as a subprocess, optionally converts the result back. Current major version 3.x. Distributed via pip.
They diverge enough that a command for one breaks on the other. Read the man page oryq --version output first — version 4.x means Go, 3.x means Python. Tutorials that show yq -y are talking about the Python version (the -y flag emits YAML output); tutorials that show yq -i are talking about the Go version (in-place edit).
The naming collision exists because both projects were independently inspired by jq and picked the obvious name. There is no plan to merge them. If you support a team with shared scripts, pin the version and document which one the scripts expect.
yq by Mike Farah (Go): YAML-native, jq-like syntax
mikefarah/yq is the default yq in Homebrew and most Linux package managers as of 2026. It is a Go binary with a YAML 1.2 parser built in, a filter language modeled on jq, and a few YAML-specific features the other tools lack — most importantly comment preservation and in-place file editing.
# Install
brew install yq # macOS
sudo snap install yq # Linux (snap)
# or download release binary from github.com/mikefarah/yq/releases
# Read a value
yq '.spec.replicas' deployment.yaml
# → 3
# Update in place (preserves comments and key order)
yq -i '.spec.replicas = 5' deployment.yaml
# Convert YAML to JSON
yq -o=json '.' deployment.yaml
# Multi-document YAML (--- separated)
yq 'select(.kind == "Service")' manifests.yamlWhat stands out:
- Single static binary — no Python interpreter, no jq dependency, ~10MB. Easy to ship in container images and CI runners.
- Comment preservation — round-tripping YAML through yq keeps inline and block comments intact. The other tools strip them.
- In-place editing with
-i— edit a file directly, likesed -i. The single feature that makes yq the default tool for CI scripts that bump Helm values or Kubernetes manifest fields. - Multi-format I/O —
-ppicks the input format (yaml, json, xml, toml, props),-opicks the output format. - YAML-specific operators —
style,tag, andanchorlet you control output formatting (block vs flow scalars, explicit type tags, anchor names).
yq by Andrey Kislyuk (Python): jq wrapper for YAML/XML/TOML
kislyuk/yq takes the opposite approach: rather than reimplement jq, it wraps it. The tool reads YAML, XML, or TOML, converts the document to JSON in memory, calls the real jq binary as a subprocess with your filter, then optionally converts the JSON output back to YAML/XML/TOML.
# Install (requires Python 3 and jq already on PATH)
pip install yq
# Read a value (output is JSON by default)
yq '.spec.replicas' deployment.yaml
# → 3
# YAML output
yq -y '.spec.replicas = 5' deployment.yaml > updated.yaml
# XML and TOML via sibling binaries
xq '.config.servers[]' config.xml
tomlq '.dependencies' Cargo.tomlWhat stands out:
- Identical jq syntax — because the filter runs in real jq, every jq feature works exactly as documented. No dialect gotchas.
- Sibling tools for other formats —
xqfor XML,tomlqfor TOML, all share the same wrapping pattern. - Requires jq on PATH — yq itself is a small Python script; you also need the jq binary installed separately.
- No in-place editing — operates on stdin/stdout. Use a temp file pattern for file mutations.
- Comments and key order are lost on YAML round-trip — the JSON intermediate strips them. Fine for read-only queries, painful for editing config files humans also read.
The Python startup tax (50–150ms per call) plus the jq subprocess cost makes kislyuk/yq the slowest of the three for batch workloads. The selling point is syntax fidelity: an existing jq pipeline drops in unchanged.
jq: JSON-only, the reference standard
jq is the original — a C binary released in 2012 with a domain-specific filter language for slicing, mapping, and transforming JSON. It is the reference both yq forks borrowed from. Every package manager ships it; every cloud CLI documents it for parsing API output.
# Install
brew install jq # macOS
sudo apt install jq # Debian/Ubuntu
sudo dnf install jq # Fedora
# Read a value
jq '.spec.replicas' deployment.json
# → 3
# Filter an array
jq '.users[] | select(.active) | .email' users.json
# Output raw strings (no quoting)
jq -r '.users[].email' users.json
# Compose with curl for API work
curl -s https://api.example.com/users | jq '.[] | .name'What jq does not do:
- Read YAML — the parser rejects anything that is not JSON. You need a converter step.
- In-place editing — like the Python yq, jq operates on stdin/stdout. Use
jq '.x = 1' file.json > tmp && mv tmp file.jsonor thespongeutility frommoreutils. - Preserve key order across edits — by default jq sorts keys alphabetically. Pass
--sort-keys=false(jq 1.7+) to preserve input order.
For everything else JSON-related, jq is the right answer. Speed, syntax stability, and documentation depth are unmatched. Compare jq with XPath-style query languages in our jq vs JSONPath guide.
Reading YAML with jq via yaml-to-json conversion
If you already have a working jq filter and just received a YAML version of the same data, the cleanest path is conversion: turn the YAML into JSON, then pipe into the existing filter. No filter rewrite needed. Several converters exist; the most common is yq itself.
# Option 1: yq as a converter (recommended)
yq -o=json '.' config.yaml | jq '.services[] | .image'
# Option 2: kislyuk/yq (default output is JSON)
yq '.' config.yaml | jq '.services[] | .image'
# Option 3: Python one-liner (no extra install if Python + PyYAML present)
python3 -c "import sys, yaml, json; json.dump(yaml.safe_load(sys.stdin), sys.stdout)" < config.yaml | jq '.services[] | .image'
# Option 4: yaml2json binary (Go, single static binary)
yaml2json < config.yaml | jq '.services[] | .image'Watch for these conversion artifacts:
- Comments disappear — JSON has no comment syntax, so the round-trip drops every YAML
#comment. See our JSON vs YAML guide for the full feature comparison. - Key order may change — depends on the converter; yq preserves order, some Python paths sort alphabetically.
- YAML-specific types collapse — timestamps, binary blobs, and custom tags lose type information and become strings.
- Multi-document YAML becomes a stream — three
---separated documents become three JSON values on stdout. Pipe intojq -s(slurp) to wrap them in an array, or process one at a time.
For writing back to YAML after editing, the conversion approach gets awkward fast. That is when you reach for mikefarah/yq with -i instead.
Modifying YAML in place: yq's special power over jq
The single capability that makes mikefarah/yq irreplaceable in ops scripts is in-place editing with comment preservation. A Kubernetes manifest, a Helm values.yaml, a GitHub Actions workflow, a Docker Compose file — these are config files written by humans, full of comments explaining why each setting exists, that scripts also need to mutate.
# Bump replica count in place — comments and key order survive
yq -i '.spec.replicas = 5' deployment.yaml
# Update a nested string
yq -i '.metadata.labels.environment = "production"' deployment.yaml
# Multiple updates in one pass
yq -i '
.spec.replicas = 5 |
.metadata.labels.environment = "production" |
.spec.template.spec.containers[0].image = "myapp:v2.1.0"
' deployment.yaml
# Add an item to an array
yq -i '.spec.template.spec.containers[0].env += [{"name": "DEBUG", "value": "true"}]' deployment.yaml
# Delete a field
yq -i 'del(.spec.template.spec.nodeSelector)' deployment.yamlThe same operation with jq or kislyuk/yq requires a temp-file dance, and the comments are lost either way:
# jq (loses comments — JSON has none, and the file would need YAML->JSON->YAML conversion)
yq -o=json '.' deployment.yaml | jq '.spec.replicas = 5' | yq -p=json -o=yaml > tmp.yaml && mv tmp.yaml deployment.yaml
# kislyuk/yq (loses comments on YAML round-trip)
yq -y '.spec.replicas = 5' deployment.yaml > tmp.yaml && mv tmp.yaml deployment.yamlIf your CI pipeline edits YAML config files, mikefarah/yq is the right default. The comment-preservation alone is worth the install. For YAML's relationship to JSON in general, our JSON to YAML tutorial covers the round-trip pitfalls in depth.
Cross-tool query examples (jq → mikefarah/yq → kislyuk/yq)
The same query in all three tools, against the same input. Path navigation and pipes are identical; the differences appear in flags, output formatting, and a handful of built-in names.
Input data (users.yaml):
users:
- name: Alice
email: alice@example.com
active: true
roles: [admin, billing]
- name: Bob
email: bob@example.com
active: false
roles: [user]
- name: Carol
email: carol@example.com
active: true
roles: [user, support]Get all active user emails:
# jq (requires YAML->JSON conversion first)
yq -o=json '.' users.yaml | jq -r '.users[] | select(.active) | .email'
# mikefarah/yq (native)
yq '.users[] | select(.active) | .email' users.yaml
# kislyuk/yq (jq syntax, YAML input)
yq -r '.users[] | select(.active) | .email' users.yamlCount users with the admin role:
# jq
yq -o=json '.' users.yaml | jq '[.users[] | select(.roles | index("admin"))] | length'
# mikefarah/yq
yq '[.users[] | select(.roles | contains(["admin"]))] | length' users.yaml
# kislyuk/yq
yq '[.users[] | select(.roles | index("admin"))] | length' users.yamlNote the small built-in difference: index works on arrays in jq and kislyuk/yq, while mikefarah/yq prefers contains for array membership. This is a typical small-paper-cut you will hit moving between Go and Python yq.
Add a new user (write back to disk):
# mikefarah/yq — in place, preserves comments
yq -i '.users += [{"name": "Dave", "email": "dave@example.com", "active": true, "roles": ["user"]}]' users.yaml
# kislyuk/yq — temp file, loses comments
yq -y '.users += [{"name": "Dave", "email": "dave@example.com", "active": true, "roles": ["user"]}]' users.yaml > tmp.yaml && mv tmp.yaml users.yaml
# jq — temp file, JSON intermediate, loses comments
yq -o=json '.' users.yaml | jq '.users += [{"name": "Dave", "email": "dave@example.com", "active": true, "roles": ["user"]}]' | yq -p=json -o=yaml > tmp.yaml && mv tmp.yaml users.yamlFor shell-script JSON parsing patterns that work with all three tools, see our Bash JSON parsing guide.
Choosing: which to install, when each shines
Most teams should install jq + mikefarah/yq as the default pair and reach for kislyuk/yq only when there is a specific reason. The Go yq covers YAML completely (including in-place edits and comment preservation) and can also process JSON, so the two binaries together handle ~99% of command-line config work without a Python dependency.
| Tool | Language | Input formats | In-place edit | Preserves YAML comments | Filter syntax | Best for |
|---|---|---|---|---|---|---|
| jq | C | JSON only | No | N/A | jq | JSON-only pipelines, API output, speed-critical loops |
| mikefarah/yq | Go | YAML, JSON, XML, TOML, props | Yes (-i) | Yes | jq-like (small dialect differences) | YAML editing, Kubernetes, Helm, GitHub Actions, mixed formats |
| kislyuk/yq | Python (wraps jq) | YAML, XML, TOML (via siblings) | No | No | Exact jq | Reusing existing jq filters against YAML/XML/TOML data |
Decision checklist:
- JSON only? →
jq. - YAML in-place edits with comments preserved? →
mikefarah/yq. - Existing jq filter against YAML/XML/TOML? →
kislyuk/yq. - Smallest container footprint? →
jq+mikefarah/yq. - CI loop running the filter thousands of times? → avoid
kislyuk/yq.
When team members reach for different tools and scripts break, pin one in CI and document the choice in the repo README.
Key terms
- jq
- The original JSON command-line processor, written in C. Reads JSON on stdin or as a file, applies a filter expression, writes JSON on stdout. Maintained at github.com/jqlang/jq, current major version 1.7+.
- mikefarah/yq
- A Go binary providing jq-like filters against YAML, JSON, XML, TOML, and properties files. Native YAML 1.2 parser with comment preservation and in-place editing (
-i). Defaultyqin Homebrew and most Linux distros as of 2026. - kislyuk/yq
- A Python wrapper around jq. Converts YAML/XML/TOML to JSON, invokes jq as a subprocess with the user's filter, optionally converts the JSON output back. Distributed via pip; ships sibling binaries
xqandtomlq. - in-place editing
- Modifying a file directly on disk rather than reading it, transforming, and writing to a new file.
mikefarah/yq -isupports this for YAML with comment and key-order preservation; jq and kislyuk/yq require a temp-file pattern. - YAML 1.2
- The current YAML specification (2009), the version both yq forks target. JSON is a strict subset of YAML 1.2, so every JSON document is valid YAML — which is why both yq tools accept JSON input transparently.
- multi-document YAML
- A single YAML file containing multiple documents separated by
---. Common in Kubernetes manifests. jq cannot represent this directly (JSON has no separator); yq tools emit a stream of JSON values thatjq -scan slurp into an array.
Frequently asked questions
Are yq and jq the same?
No. jq is a JSON-only command-line processor written in C, originally by Stephen Dolan and now maintained by a community team. yq is the YAML-focused cousin — but there are two completely separate projects both named yq. The first, mikefarah/yq, is a Go binary written from scratch with jq-like filter syntax and native YAML support. The second, kislyuk/yq, is a Python wrapper that shells out to jq under the hood and adds YAML, XML, and TOML conversion on top. They share the same name and a roughly similar mission (run jq-style queries against YAML) but are otherwise independent codebases with different syntax dialects, install commands, and feature sets. When a tutorial or Stack Overflow answer mentions yq, the first thing to check is which fork the author has in mind — the commands are not always interchangeable.
Which yq should I install — Go or Python?
For most users in 2026, mikefarah/yq (the Go version) is the better default. It is faster (no Python startup, no jq subprocess), has zero runtime dependencies (single static binary), supports in-place YAML editing with -i (a feature kislyuk/yq does not have), and preserves YAML comments and key order on round-trip — important when editing config files like Kubernetes manifests or GitHub Actions workflows. Install it with brew install yq on macOS or apt install yq / the release binary on Linux. Pick kislyuk/yq instead when you already know jq syntax cold and want exact jq-identical filters with no relearning, or when you need to process XML or TOML in the same pipeline (it converts both to JSON before applying jq). The two can coexist on the same machine if you rename one of the binaries.
Can jq read YAML files directly?
No. jq 1.7+ does not read YAML natively — it expects JSON on stdin or as a file argument, and anything else fails the parser. The standard workaround is to convert YAML to JSON first using a separate tool, then pipe the JSON into jq. Common converters include yq itself (yq -o=json file.yaml), Python (python -c "import sys, yaml, json; json.dump(yaml.safe_load(sys.stdin), sys.stdout)"), or the dedicated yaml2json binary. If your workflow is mostly YAML, skip the conversion step and use yq directly — it accepts both YAML and JSON input and can output either. The conversion approach makes sense only when you already have a long, working jq filter and just need to feed it YAML data without rewriting the filter for yq.
How do I modify a YAML field with yq?
With mikefarah/yq, use the -i flag to edit a file in place: yq -i '.spec.replicas = 3' deployment.yaml updates the replicas field and writes the file back with comments and key order preserved. For multi-line updates, chain expressions with the pipe operator: yq -i '.metadata.labels.env = "prod" | .spec.replicas = 5' deployment.yaml. The kislyuk/yq variant does not support in-place editing — it operates on stdin/stdout, so you need a temporary file dance: yq -y '.spec.replicas = 3' deployment.yaml > tmp.yaml && mv tmp.yaml deployment.yaml. This in-place capability is the single most common reason teams pick mikefarah/yq over the Python version for ops scripts that touch Kubernetes manifests, Helm values, Docker Compose files, and GitHub Actions workflows.
What's the syntax difference between Go yq and jq?
They look similar but diverge on assignment, function names, and built-ins. Path navigation is identical (.users[0].name works in both). Pipes are identical (.users | length). Where they differ: assignment in mikefarah/yq uses = and |= directly on paths (.spec.replicas = 3), while jq requires update syntax (.spec.replicas = 3 also works in jq, but more complex updates often use |= or with_entries). String interpolation in mikefarah/yq uses + concatenation (.name + " v" + .version), while jq supports \(expr) inline. Some built-ins differ in name or signature — yq has style and tag operators for controlling YAML output (single quotes, double quotes, block scalars) that jq lacks entirely. For straightforward filters (selection, indexing, mapping), copy-pasting between the two usually works; for complex transformations, expect to consult both manuals.
Can yq handle JSON too?
Yes — both yq forks accept JSON input and can emit JSON output. mikefarah/yq auto-detects the input format and lets you pick the output with -o=json (or -o=yaml, -o=xml, -o=toml, -o=props). kislyuk/yq is built around jq, so it always thinks in JSON internally; it reads YAML/XML/TOML and converts to JSON, runs your filter, then converts back with -y (YAML), -x (XML), or default JSON. This means you can use either yq as a drop-in jq replacement for JSON-only pipelines if you happen to have it installed, though the performance cost is small and the dependency footprint is larger. For pure JSON work jq is still the right answer, but the yq tools are useful when a script needs to handle whichever format shows up.
Which tool is faster — yq or jq?
For JSON-only workloads, jq is the fastest of the three by a comfortable margin — it is a C binary with a hand-tuned parser and a streaming evaluation model. mikefarah/yq (Go) is close behind on JSON and the fastest of the three for YAML, since it skips the conversion step. kislyuk/yq (Python) is consistently the slowest because every invocation pays Python startup time (50–150ms) plus the cost of converting YAML to JSON before calling jq as a subprocess. The differences only matter in CI loops or scripts that invoke the tool thousands of times — for interactive use or one-off filters, all three feel instant. If a build step processes hundreds of YAML files in a loop, switching from kislyuk/yq to mikefarah/yq can cut total time by an order of magnitude purely from skipping the Python and subprocess overhead.
Is there a yq for TOML and XML?
Yes — kislyuk/yq ships with sibling binaries tomlq and xq that work the same way: read TOML or XML, convert to JSON, run a jq filter, and optionally convert back. mikefarah/yq supports the same formats via -p (input) and -o (output) flags within the single yq binary — yq -p=toml -o=json file.toml or yq -p=xml -o=yaml file.xml. The XML support in both tools goes through a JSON conversion, so attributes become @-prefixed keys and text nodes become #text or +content, which can surprise people used to XPath. For heavy XML work, a dedicated XPath tool like xmlstarlet is usually a better fit; the yq tools are best for light XML inspection and conversion as part of mixed-format pipelines.
Further reading and primary sources
- mikefarah/yq on GitHub — The Go YAML processor — release binaries, full operator reference, and recipe cookbook
- kislyuk/yq on GitHub — The Python jq wrapper with YAML/XML/TOML support and sibling xq/tomlq binaries
- jq Manual — The authoritative jq filter reference — built-ins, operators, and language semantics
- YAML 1.2 Specification — The spec both yq forks implement — useful when YAML quirks surprise you
- jqlang/jq on GitHub — The community-maintained jq repository (the project moved here after the 1.6 era hiatus)