Skip to content

tobiash/k8q

Repository files navigation

k8q

A Unix-style pipe for filtering, mutating, and exploring streams of Kubernetes YAML manifests.

k8q reads YAML from stdin, applies transformations, and writes the result to stdout. It understands Kubernetes semantics — kind matching, API groups, workload pod templates — so you can manipulate manifests safely without breaking formatting or losing comments.

Output is automatically formatted: fields are reordered to follow Kubernetes conventions (apiVersion, kind, metadata, spec, status), and colorized when writing to a terminal.

Built on kustomize/kyaml for lossless AST-based YAML manipulation.

Install

go install github.com/tobiash/k8q@latest

Commands

get -- Filter manifests

Filters the stream to keep only manifests matching the given criteria. All provided criteria must match (AND semantics).

# Get all ConfigMaps
helm template my-chart | k8q get --kind ConfigMap

# Get a specific deployment by name
helm template my-chart | k8q get --kind Deployment --name my-app

# Filter by API group
helm template my-chart | k8q get --group apps

# Filter by label selector
helm template my-chart | k8q get -l app=web

drop — Remove manifests

Filters the stream to remove manifests matching the given criteria. Manifests matching ANY provided criterion are dropped (OR semantics).

# Remove all Flux CD resources by API group
kustomize build . | k8q drop --group toolkit.fluxcd.io

# Remove resources by multiple criteria (OR)
helm template my-chart | k8q drop --group cert-manager.io --kind ConfigMap

# Use label selectors
kustomize build . | k8q drop -l 'tier in (frontend,backend)'

subst — Substitute environment variables

Replaces ${VAR} references in the raw YAML stream using values from a .env file, before parsing.

# manifest.yaml contains ${DB_HOST} and ${DB_PORT}
cat manifest.yaml | k8q subst --env-file .env

# Works with multi-document streams
helm template my-chart | k8q subst --env-file production.env

label — Inject labels

Adds a label to metadata.labels on matching manifests. For workload kinds (Deployment, DaemonSet, StatefulSet, Job), the label is also injected into spec.template.metadata.labels.

# Label everything
helm template my-chart | k8q label app.kubernetes.io/managed-by=k8q

# Label only Deployments
helm template my-chart | k8q label app=web --kind Deployment

annotate — Inject annotations

Adds an annotation to metadata.annotations on matching manifests.

# Inject an annotation
kustomize build . | k8q annotate reloader.stakater.com/auto=true --kind Deployment

set-image — Update container images

Updates container images in matching manifests (Pods, Deployments, StatefulSets, DaemonSets, Jobs, CronJobs).

# Update a specific container image
helm template my-chart | k8q set-image my-app=my-registry.io/app:v2.0.0

set-namespace — Overwrite namespace

Sets metadata.namespace on matching manifests.

# Move resources from 'default' to 'production'
helm template my-chart | k8q set-namespace production --namespace default

patch — Merge YAML snippets

Deep-merges a YAML snippet into matching manifests.

# Add a nodeSelector to all StatefulSets
k8q patch 'spec: { template: { spec: { nodeSelector: { disk: ssd } } } }' --kind StatefulSet

remove — Delete fields

Deletes a field from matching manifests using a dot-separated path.

# Remove clusterIP from Services
k8q remove spec.clusterIP --kind Service

scale — Update replicas

Updates spec.replicas for matching manifests.

# Scale down everything in a namespace
k8q scale 0 --namespace dev

rename — Prefix/Suffix names

Modifies metadata.name by adding a prefix or suffix.

# Add a suffix to all resources
k8q rename --suffix "-v2"

diff — Compare manifests

Compares two sets of Kubernetes manifests semantically. Resources are matched by identity (apiVersion + kind + namespace + name), so differences in document order or field ordering are ignored.

# Compare two files
k8q diff before.yaml after.yaml

# Pipe as "after", file as "before"
helm template my-chart | k8q diff --base before.yaml

# Summary mode
k8q diff before.yaml after.yaml --summary

Exit codes: 0 = identical, 1 = differences found, 2+ = error.

File Input (--file, -f)

Instead of piping YAML through stdin, you can read directly from a file:

k8q get --kind Deployment --file manifest.yaml
k8q diff --file before.yaml --file after.yaml

Structured Output (--output json)

All commands that produce YAML or reports support --output json for machine-readable, agent-friendly output:

k8q get --kind ConfigMap --output json
k8q count --group-by-kind --output json
k8q diff before.yaml after.yaml --output json
k8q sum --output json

JSON output follows Kubernetes API conventions:

  • Lists are wrapped in a v1/List envelope with apiVersion, kind, and items
  • Resource references use ObjectRef (apiVersion, kind, name, namespace)
  • Quantities are rendered as Kubernetes quantity strings (e.g., "200m", "512Mi")

Semantic Exit Codes

k8q uses semantic exit codes for reliable automation:

Code Meaning
0 Success (no differences for diff)
1 Differences found (diff only)
2 User input error (bad args, missing file, invalid config)

Programmatic Discovery (describe)

The hidden describe command emits a JSON description of the CLI for agent consumption:

k8q describe

This includes all commands, flags, descriptions, idempotency, and side-effect metadata.

serve — Mock API server

Starts an in-process mock Kubernetes API server that serves piped-in manifests over HTTPS. Writes a kubeconfig pointing to the server, then optionally executes a command with KUBECONFIG set. This lets cluster scanning tools connect to k8q as if it were a real cluster.

# Run kubectl against piped manifests
helm template my-chart | k8q serve -- kubectl get deployments

# Run popeye against a Helm chart
helm template my-chart | k8q serve -- popeye

# Run kubent against a Kustomize build
kustomize build . | k8q serve -- kubent --cluster

# Run trivy against manifests
cat manifests.yaml | k8q serve -- trivy k8s --context k8q

# Interactive mode — prints kubeconfig path, waits for Ctrl+C
cat manifests.yaml | k8q serve

No cluster required. Pipe in manifests, run the tool, done.

Flags:

Flag Shorthand Description
--port -p Port to bind (default: random ephemeral)
-- Separator; everything after is the command to exec

k8q exits with the child command's exit code. In interactive mode, press Ctrl+C to stop.

Analyzers

Analyzers provide insights about the stream. They typically terminate the pipeline by printing a report instead of YAML.

count — Count manifests

Counts matching manifests.

# Count everything
k8q count

# Count only Deployments
k8q count --kind Deployment

# Group counts by kind
k8q count --group-by-kind

sum — Sum resources

Calculates total CPU and Memory requests for matching manifests (looking in Pod templates). Accounts for spec.replicas.

# Sum resources for all workloads
k8q sum

# Sum resources for a specific namespace
k8q sum --namespace production

# Fail if any container is missing requests or limits
k8q sum --require-requests --require-limits

# Assert total resources are within bounds (exits 1 if exceeded)
k8q sum --max-cpu-requests 2000m --max-mem-limits 10Gi

Match Criteria

Both filtering (get, drop) and mutation commands support the same matching filters:

Flag Shorthand Description
--kind Kubernetes Kind (case-insensitive)
--name Resource name (exact)
--namespace -n Resource namespace (exact)
--group -g API group (substring match)
--selector -l Kubernetes label selector
(positional) kind, kind/name, or api-group

For get, label, annotate, etc., multiple criteria are combined with AND. For drop, multiple criteria are combined with OR.

Matching is optional for mutators (they match everything by default) but required for filters.

Label Selectors

get and drop support Kubernetes-style label selectors via the -l / --selector flag. The syntax matches kubectl:

Composition

Commands compose naturally through pipes:

# Get a deployment, labeled, in the right namespace
helm template my-chart \
  | k8q get --kind Deployment --name my-app \
  | k8q label app.kubernetes.io/managed-by=k8q \
  | k8q set-namespace production

# Strip Flux resources, substitute env vars, and apply
kustomize build . \
  | k8q drop --group toolkit.fluxcd.io \
  | k8q subst --env-file .secrets \
  | kubectl apply -f -

Why not yq?

yq is a general-purpose YAML processor. k8q is purpose-built for Kubernetes manifests:

  • Kind-aware filtering -- k8q get --kind Deployment knows what a Deployment is.
  • Label selectors -- -l app=web,env!=staging with full Kubernetes selector syntax.
  • Workload-aware labeling -- automatically propagates labels to pod templates.
  • API group matching -- k8q drop --group toolkit.fluxcd.io filters by group, not string matching.
  • Canonical field ordering -- output is automatically sorted: apiVersion, kind, metadata, spec, status.
  • Colorized output -- syntax-highlighted YAML in the terminal, disabled when piped.
  • Formatting preservation -- uses kyaml's AST, so comments survive.

Tech Stack

Component Library
CLI framework alecthomas/kong
YAML engine kustomize/kyaml
API server net/http + k8s.io/apimachinery (zero additional dependencies)
Env substitution drone/envsubst

Shell Completion

To enable shell completion, add the following to your shell profile (.bashrc or .zshrc):

Bash:

source <(k8q completion -c bash)

Zsh:

source <(k8q completion -c zsh)

License

See LICENSE.

About

CLI tool to filter/query Kubernetes manifest YAML

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages