YQ the tool
yq is a lightweight, portable command-line YAML, JSON, and XML processor. It is essential for DevOps and SRE engineers working with Kubernetes, Terraform, Ansible, and CI/CD pipelines across AWS, Azure, GCP, Linux, NixOS, and WSL environments.
Why Use yq in DevOps & SRE?
- Automate YAML/JSON edits: Update Kubernetes manifests, Terraform variables, and CI/CD configs programmatically.
- Bulk Operations: Apply changes across multiple files for GitOps, policy enforcement, or compliance.
- CI/CD Integration: Use yq in GitHub Actions, Azure Pipelines, or GitLab CI/CD for validation, patching, and templating.
- Cloud-Native: Works seamlessly with cloud IaC and configuration workflows.
Real-Life Examples
1. Update Image Tag in All Kubernetes Deployments
grep -rl 'image:' ./k8s | xargs -I{} yq -i '.spec.template.spec.containers[0].image = "nginx:1.25.0"' {}
2. Extract All Resource Limits for Audit
find ./manifests -name '*.yaml' | xargs -I{} yq '.spec.template.spec.containers[].resources.limits' {}
3. Patch a Value in a CI/CD Pipeline (GitHub Actions)
- name: Patch image tag in deployment
run: yq -i '.spec.template.spec.containers[0].image = "myrepo/app:${{ github.sha }}"' k8s/deployment.yaml
4. Merge Multiple YAML Files for GitOps
yq ea '. as $item ireduce ({}; . * $item )' overlays/*.yml > merged.yaml
5. Use Environment Variables for Dynamic Values
export VERSION=1.2.3
yq -i '.app.version = strenv(VERSION)' values.yaml
Best Practices (2025)
- Always validate YAML after edits:
kubectl apply --dry-run=client -f file.yaml - Use yq in CI/CD for repeatable, automated changes
- Document yq commands in README or pipeline logs
- Prefer explicit paths to avoid accidental overwrites
- Use yq with version control for traceability
Common Pitfalls
- Overwriting files without backup (
-iis destructive) - Not validating YAML after bulk edits
- Using ambiguous paths (be specific to avoid wrong fields)
- Forgetting to quote strings with special characters
Install
Download the latest binary
wget
Use wget to download, gzipped pre-compiled binaries:
For instance, VERSION=v4.2.0 and BINARY=yq_linux_amd64
Compressed via tar.gz
sh
wget https://github.com/mikefarah/yq/releases/download/${VERSION}/${BINARY}.tar.gz -O - |\
tar xz && mv ${BINARY} /usr/bin/yq
plaintext
Plain binary
sh
wget https://github.com/mikefarah/yq/releases/download/${VERSION}/${BINARY} -O /usr/bin/yq &&\
chmod +x /usr/bin/yq
plaintext
Latest version
sh
wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/bin/yq &&\
chmod +x /usr/bin/yq
plaintext
MacOS / Linux via Homebrew:
Using Homebrew
shell
brew install yq
plaintext
Linux via snap:
shell
snap install yq
plaintext
Snap notes
yq installs with strict confinement in snap, this means it doesn’t have direct access to root files. To read root files you can:
sh
sudo cat /etc/myfile | yq '.a.path'
plaintext
And to write to a root file you can either use sponge:
shell
sudo cat /etc/myfile | yq '.a.path = "value"' | sudo sponge /etc/myfile
plaintext
or write to a temporary file:
sh
sudo cat /etc/myfile | yq '.a.path = "value"' | sudo tee /etc/myfile.tmp
sudo mv /etc/myfile.tmp /etc/myfile
rm /etc/myfile.tmp
plaintext
Run with Docker or Podman
Oneshot use:
sh
docker run --rm -v "${PWD}":/workdir mikefarah/yq [command] [flags] [expression ]FILE...
plaintext
Note that you can run yq in docker without network access and other privileges if you desire, namely --security-opt=no-new-privileges --cap-drop all --network none.
shell
podman run --rm -v "${PWD}":/workdir mikefarah/yq [command] [flags] [expression ]FILE...
plaintext
Pipe in via STDIN:
You’ll need to pass the -i\--interactive flag to docker:
sh
docker run -i --rm mikefarah/yq '.this.thing' < myfile.yml
plaintext
sh
podman run -i --rm mikefarah/yq '.this.thing' < myfile.yml
plaintext
Run commands interactively:
sh
docker run --rm -it -v "${PWD}":/workdir --entrypoint sh mikefarah/yq
plaintext
sh
podman run --rm -it -v "${PWD}":/workdir --entrypoint sh mikefarah/yq
plaintext
It can be useful to have a bash function to avoid typing the whole docker command:
shell
yq() {
docker run --rm -i -v "${PWD}":/workdir mikefarah/yq "$@"
}
plaintext
shell
yq() {
podman run --rm -i -v "${PWD}":/workdir mikefarah/yq "$@"
}
plaintext
Running as root:
yq’s container image no longer runs under root (#860). If you’d like to install more things in the container image, or you’re having permissions issues when attempting to read/write files you’ll need to either:
sh
docker run --user="root" -it --entrypoint sh mikefarah/yq
plaintext
sh
podman run --user="root" -it --entrypoint sh mikefarah/yq
plaintext
Or, in your Dockerfile:
```shell FROM mikefarah/yq
USER root RUN apk add –no-cache bash USER yq ```plaintext
Missing timezone data
By default, the alpine image yq uses does not include timezone data. If you’d like to use the tz operator, you’ll need to include this data:
```shell FROM mikefarah/yq
USER root RUN apk add –no-cache tzdata USER yq ```plaintext
Podman with SELinux
If you are using podman with SELinux, you will need to set the shared volume flag :z on the volume mount:
sh
-v "${PWD}":/workdir:z
plaintext
GitHub Action
```shell
- name: Set foobar to cool uses: mikefarah/yq@master with: cmd: yq -i ‘.foo.bar = “cool”’ ‘config.yml’
- name: Get an entry with a variable that might contain dots or spaces id: get_username uses: mikefarah/yq@master with: cmd: yq ‘.all.children.[”${{ matrix.ip_address }}”].username’ ops/inventories/production.yml
- name: Reuse a variable obtained in another step run: echo ${{ steps.get_username.outputs.result }} ```plaintext
See https://mikefarah.gitbook.io/yq/usage/github-action for more.
Go Install:
sh
go install github.com/mikefarah/yq/v4@latest
plaintext
yq Joke: Why did the SRE love yq? Because it could fix YAML faster than they could break it!