Use yq to Process YAML
Learn to read, filter, transform, and safely edit YAML files in place using mikefarah/yq — the Go-based jq equivalent for YAML.
Before you start
- ▸Basic familiarity with YAML syntax (keys, values, lists, nesting)
- ▸A terminal with sudo access for installation
- ▸curl or wget available for binary download if not using a package manager
yq is a lightweight, portable command-line YAML processor written in Go by Mike Farah. Think of it as jq for YAML — you can read, filter, update, and transform YAML files without writing a single line of Python or a throwaway script. This guide covers installation, the expression syntax, common transforms, and safe in-place editing.
Installation
There are several yq packages floating around. Make sure you install mikefarah/yq, not the Python-based yq wrapper that some distro repos still ship. The canonical way to get the right one is via the GitHub release binary or a package manager that tracks it explicitly.
Debian / Ubuntu
The version in older Ubuntu repos is the Python wrapper. Use the upstream binary instead:
sudo wget -qO /usr/local/bin/yq \
https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
sudo chmod +x /usr/local/bin/yq
yq --version
Fedora / RHEL / Rocky
Fedora 36+ ships mikefarah/yq in the default repos:
sudo dnf install yq
On RHEL 8/9 or Rocky without the package, fall back to the binary method shown above.
Arch Linux
sudo pacman -S go-yq
The package is named go-yq to distinguish it from the Python variant.
Verify the right yq
yq --version
# Should print: yq (https://github.com/mikefarah/yq/) version v4.x.x
If you see a Python-based version string, remove it and install the binary manually.
Expression Basics
All yq expressions use a path syntax similar to jq. A dot (.) is the root document. Nested keys are accessed with dots: .spec.replicas. Arrays are zero-indexed: .items[0]. Wildcards and recursive descent (..) are supported.
Given this example file, app.yaml:
cat app.yaml
name: myapp
version: "1.4.2"
server:
host: 0.0.0.0
port: 8080
tls: false
features:
- auth
- metrics
- tracing
Read a single value
yq '.server.port' app.yaml
# 8080
Read a nested array element
yq '.features[1]' app.yaml
# metrics
Read all array elements
yq '.features[]' app.yaml
Common Transforms
Update a scalar value
Use the assignment operator = with the -e flag (evaluate) or pipe the result. To print the modified document without touching the file:
yq '.server.port = 9090' app.yaml
Toggle a boolean
yq '.server.tls = true' app.yaml
Add an item to an array
yq '.features += ["ratelimit"]' app.yaml
Delete a key
yq 'del(.server.tls)' app.yaml
Rename a key
yq does not have a direct rename operator. The idiomatic approach is to copy then delete:
yq '(.server.hostname = .server.host) | del(.server.host)' app.yaml
Merge two YAML files
Pass multiple files; yq processes them as a stream. To merge a patch file on top of a base:
yq '. *= load("patch.yaml")' base.yaml
The *= operator does a recursive merge, with right-hand values winning on conflicts.
Select array elements by condition
yq '.features[] | select(. == "auth")' app.yaml
Convert YAML to JSON
yq -o=json '.' app.yaml
Convert JSON to YAML
yq -p=json '.' data.json
Output a specific key as plain string (no quotes)
yq -r '.version' app.yaml
# 1.4.2
Editing Files In Place
The -i flag writes the result back to the original file. This is the most common source of mistakes — always test your expression without -i first.
Basic in-place update
yq -i '.server.port = 9090' app.yaml
Back up before editing
yq does not have a built-in backup suffix like sed -i.bak. Do it manually:
cp app.yaml app.yaml.bak
yq -i '.version = "1.5.0"' app.yaml
Edit multiple files at once
yq -i '.server.tls = true' config/*.yaml
Scripted multi-step edits
Chain expressions with | inside a single call to avoid multiple file passes:
yq -i '
.version = "1.5.0" |
.server.port = 9090 |
.server.tls = true
' app.yaml
Working with Multi-Document YAML
Kubernetes manifests and many other files contain multiple documents separated by ---. yq handles them natively.
Select a specific document by index
yq 'select(di == 1)' multi.yaml
Update a field across all documents
yq -i '[.] | .[] | select(.kind == "Deployment") .spec.replicas = 3' k8s.yaml
Practical Kubernetes example: bump an image tag
yq -i '(
select(.kind == "Deployment") |
.spec.template.spec.containers[] |
select(.name == "app")
).image = "myrepo/app:v2.1.0"' deployment.yaml
Verification
After any in-place edit, validate the file is still valid YAML and contains what you expect:
# Check it parses without error
yq '.' app.yaml > /dev/null && echo "Valid YAML"
# Spot-check the changed value
yq '.server.port' app.yaml
# 9090
Troubleshooting
Expression silently returns nothing
A path that does not exist returns null, not an error. Double-check your key names — YAML is case-sensitive. Use yq 'keys' app.yaml to list top-level keys and work down.
In-place edit produces an empty file
This almost always means the expression itself errored out. Run the same expression without -i first. If the terminal shows nothing or an error message, fix the expression before adding -i back.
Comments are stripped after editing
yq v4 preserves comments in most cases, but complex merges or expression chains can drop them. If comment preservation is critical, review the diff after editing and keep your expressions simple.
Type coercion surprises
YAML values like true, yes, and on are all parsed as booleans. If you need a literal string "true", quote it in your expression: .flag = "true". Check with yq '.flag | type' app.yaml.
Frequently asked questions
- How do I tell if I have mikefarah/yq or the Python yq wrapper installed?
- Run yq --version. The mikefarah version prints a URL containing github.com/mikefarah/yq and a v4.x.x version number. The Python wrapper prints something like yq x.x.x and references kislyuk/yq or a pip origin.
- Does yq -i preserve YAML comments?
- yq v4 makes a best effort to preserve comments, and succeeds in most straightforward edits. Complex chained expressions or merges can sometimes drop comments, so diff the file afterward if comments matter.
- Can I use yq to edit JSON files directly?
- Yes. Pass -p=json to parse JSON input and -o=json to emit JSON. You can then use the same path expressions you would on YAML.
- How do I update a value only if the key already exists?
- Wrap the assignment in a select: yq '(select(has("version")) | .version) = "2.0"' file.yaml — this silently skips files that lack the key.
- Is there a way to validate YAML structure with yq?
- yq itself only checks that a file is syntactically valid YAML; it has no schema validation. Pipe yq output to a dedicated tool like ajv or kubeconform for schema checks.
Related guides
Bash Arrays and Associative Arrays
Master bash indexed and associative arrays: declaration, element access, looping, mapfile, namerefs, and practical patterns for real scripting work.
Bash Functions and Variable Scoping
Master Bash function scoping with local variables, source-based libraries, correct use of return codes, and array passing techniques including namerefs.
Bash Loops: for, while and until
Learn all three Bash loop types — for, while, and until — with practical, copy-paste examples covering file iteration, counting, polling, and safe line reading.
Bash Scripting for Beginners
Learn Bash scripting from scratch: shebang lines, variables, conditionals, loops, and arguments, plus a real backup script to tie it all together.