yq eval: Template and Merge YAML Values
yq eval can act as a YAML templater: it injects environment values with env()/strenv() and merges base plus overlay documents into a final manifest.
Beyond querying, yq (mikefarah/yq v4) rewrites YAML in place. That makes it a structure-aware way to inject config values without the indentation risks of text templating. This is distinct from plain yq querying: here yq is the renderer.
What it does
yq eval evaluates an expression against a YAML document. env("VAR") and strenv(VAR) pull environment values into fields, and the * merge operator with eval-all combines a base document and an overlay, letting you template a manifest from parts.
Common usage
# inject an env value as a typed field
IMAGE=nginx:1.27 yq eval '.spec.image = strenv(IMAGE)' base.yaml > out.yaml
# merge a base and an environment overlay
yq eval-all '. as $item ireduce ({}; . * $item)' base.yaml prod.yaml > out.yaml
# simpler two-file merge
yq eval-all 'select(fi==0) * select(fi==1)' base.yaml prod.yamlOptions
| Construct | What it does |
|---|---|
| env("VAR") | Insert an env value, type-inferred (numbers become numbers) |
| strenv(VAR) | Insert an env value always as a string |
| eval-all | Load all documents/files before evaluating (needed to merge) |
| * (merge) | Deep-merge operator to overlay one document onto another |
| -i, --inplace | Rewrite the input file in place |
In CI
Use strenv() for values like image tags so 1.27 is not coerced to a number and requoted oddly; use env() only when you want numeric or boolean typing. Prefer merging small overlays over sprawling text templates: yq keeps output valid YAML. Render to an artifact and diff before apply. Pin to yq v4 syntax, since v3 used a completely different command shape.
Common errors in CI
"Error: Bad expression, could not find matching characters" is a v4 expression typo (or v3 syntax run under v4). A field set to null after env() usually means the variable was unset, since env() of a missing var yields null; guard with // "default". Merging without eval-all only sees the first document, so the overlay appears to do nothing.