Anurag's Notes

Other Topics

← 14 Kustomize Basics (2025 Updates) · Index · 16 Mock Exams →

Contents


15.1 JSON Path in Kubernetes

Source: KodeKloud notes

Why JSONPath with kubectl

  • Production clusters hold hundreds of Nodes and thousands of Pods, Deployments, ReplicaSets, Services, Secrets. You need precise, scriptable views of them.
  • kubectl is a client. The kube-apiserver returns every resource as JSON, and kubectl turns that JSON into a default table that hides most fields.
  • -o wide adds a few columns but still drops most fields. kubectl describe gives a detailed human view. JSONPath extracts exactly the fields you want.
  • Use it to pull specific fields, reformat output for scripts, reports and monitoring checks, and build compact tables without external tools like jq.
  • Prerequisite: basic JSONPath syntax. The course points to a JSONPath beginners video plus KodeKloud JSONPath practice tests, including one on a Kubernetes dataset.
kubectl get nodes            # default table: NAME STATUS ROLES AGE VERSION
kubectl get nodes -o json    # full JSON returned by the API server (also -o=json)

Trimmed kubectl get nodes -o json, showing the structure that every path starts from:

{
  "apiVersion": "v1",
  "items": [
    {
      "apiVersion": "v1",
      "kind": "Node",
      "metadata": {
        "annotations": { "node.alpha.kubernetes.io/ttl": "0" },
        "creationTimestamp": "2019-06-21T09:01:56Z",
        "labels": {
          "beta.kubernetes.io/arch": "amd64",
          "beta.kubernetes.io/os": "linux",
          "kubernetes.io/hostname": "master",
          "node-role.kubernetes.io/master": ""
        }
      },
      "status": {
        "capacity": { "cpu": "4" },
        "nodeInfo": { "architecture": "amd64" }
      }
    }
  ]
}

[!CAUTION] Correction: the course sample is from an old cluster (v1.11.3, ROLES master). Current clusters use the label and taint node-role.kubernetes.io/control-plane, and the beta.kubernetes.io/arch and beta.kubernetes.io/os labels are deprecated in favour of kubernetes.io/arch and kubernetes.io/os. The JSON structure and the paths are otherwise the same.

Common report targets (per-item paths)

ReportColumns → path
Node name + CPUNODE → .metadata.name, CPU → .status.capacity.cpu
Node name + taintsNAME → .metadata.name, TAINTS → .spec.taints
Node name + architectureNAME → .metadata.name, ARCH → .status.nodeInfo.architecture
Pod name + imagePOD → .metadata.name, IMAGE → .spec.containers[*].image

4-step workflow

  1. Pick the kubectl command that returns the objects, e.g. kubectl get nodes or kubectl get pods --all-namespaces.
  2. Look at the raw JSON with kubectl get <resource> -o json to find the path.
  3. Write the JSONPath expression. You can test it in an evaluator such as jsonpath.com by pasting the -o json output.
  4. Run it: kubectl get <resource> -o jsonpath='<expression>'.

Basic queries

  • With -o jsonpath, the path starts at the root of the List. Prefix it with .items[N] or .items[*], and wrap each expression in { }.
  • [*] returns every element. The values are printed space-separated on one line.
# Single value: first pod, first container image
kubectl get pods -o=jsonpath='{.items[0].spec.containers[0].image}'
# nginx:alpine

# All items
kubectl get nodes -o=jsonpath='{.items[*].metadata.name}'                 # master node01
kubectl get nodes -o=jsonpath='{.items[*].status.nodeInfo.architecture}'  # amd64 amd64
kubectl get nodes -o=jsonpath='{.items[*].status.capacity.cpu}'           # 4 4

# Multiple expressions in one string are printed one after another
kubectl get nodes -o=jsonpath='{.items[*].metadata.name} {.items[*].status.capacity.cpu}'
# master node01 4 4

Formatting: newline and tab

  • Put literal characters inside the expression string: {"\n"} for a newline, {"\t"} for a tab.
kubectl get nodes -o=jsonpath='{.items[*].metadata.name} {"\n"} {.items[*].status.capacity.cpu}'
# master node01
#  4 4
  • The output is still grouped by field (all names, then all CPUs). To get one row per object, use range.

Loops: range … end

  • Pattern: {range .items[*]} → fields relative to the current item ({.metadata.name}) → separators {"\t"} / {"\n"} → {end}.
kubectl get nodes -o=jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.capacity.cpu}{"\n"}{end}'
# master  4
# node01  4

Custom columns: -o custom-columns

  • An easier way to get columns without writing a loop. The format is COLUMN_NAME:<path> pairs separated by commas, with no spaces.
  • Each path is relative to each item, so leave out .items[*] and the { }. kubectl does the looping.
kubectl get nodes -o=custom-columns=NODE:.metadata.name,CPU:.status.capacity.cpu
# NODE     CPU
# master   4
# node01   4

Sorting: --sort-by

  • --sort-by=<per-item path> uses the same relative path as custom-columns. It can be combined with any output format.
kubectl get nodes --sort-by=.metadata.name -o=custom-columns=NODE:.metadata.name,CPU:.status.capacity.cpu

JSON List root vs per-item: -o jsonpath paths start at the root and need .items[*] or range, while custom-columns and --sort-by paths are relative to each item Diagram (new): where each kubectl output path starts. -o jsonpath starts at the List root, while custom-columns and --sort-by start at each item.

Quick reference

OptionPurposeExample
-o jsonRaw JSON from the API serverkubectl get pods -o json
-o jsonpath='<expr>'Extract fields (path from root)kubectl get nodes -o jsonpath='{.items[*].metadata.name}'
-o custom-columns=Per-item column tablekubectl get pods -o custom-columns=POD:.metadata.name,IMAGE:.spec.containers[*].image
--sort-by=<path>Sort by a per-item fieldkubectl get nodes --sort-by=.metadata.name

Shell quoting and best practices

  • Put the whole expression in single quotes, -o jsonpath='...', so the shell does not expand $, *, [ ] or backticks. If you have to use double quotes, escape the inner $ and backticks. If the expression itself needs quotes (string literals, filters), use double quotes inside the single-quoted string. Wrong quoting is the most common cause of errors.
  • Always check the path in -o json first.
  • Use custom-columns for simple tables. Use jsonpath with range for complex or mixed output.
  • Test expressions interactively before you put them in scripts.

Practice exercises from the course

[!NOTE] Beyond the course: the commands below supply solutions to the course’s exercise statements.

# 1. Every pod name and its image(s) in a namespace
kubectl get pods -n <ns> -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[*].image}{"\n"}{end}'

# 2. Pod, Node, restart counts
kubectl get pods -o custom-columns=POD:.metadata.name,NODE:.spec.nodeName,RESTARTS:.status.containerStatuses[*].restartCount

# 3. Nodes sorted by CPU capacity
kubectl get nodes --sort-by=.status.capacity.cpu -o custom-columns=NODE:.metadata.name,CPU:.status.capacity.cpu

[!NOTE] Beyond the course: extra JSONPath patterns that come up in labs and the exam.

  • Filter ?(@.field=="value"): kubectl get nodes -o jsonpath='{.items[*].status.addresses[?(@.type=="InternalIP")].address}'
  • kubeconfig queries work too: kubectl config view --kubeconfig=/root/my-kube-config -o jsonpath='{.contexts[?(@.context.user=="aws-user")].name}'. Add --raw if you need the certificate data, which is otherwise redacted.
  • Nested range: '{range .items[*]}{.metadata.name}{":"}{range .spec.containers[*]}{" "}{.image}{end}{"\n"}{end}'
  • Keys that contain dots must be escaped: '{.items[*].metadata.labels.kubernetes\.io/hostname}'. The same applies in custom-columns.
  • The leading $ is optional ({$.items[*]...} is the same as {.items[*]...}). -o jsonpath prints no trailing newline, so add {"\n"} if you need one.
  • --no-headers removes the header row from custom-columns. -o custom-columns-file=<file> reads the column spec from a file.
  • Common sorts: PVs by size kubectl get pv --sort-by=.spec.capacity.storage; objects by age --sort-by=.metadata.creationTimestamp.
  • Exam tasks often ask you to save the output to a file, e.g. ... -o jsonpath='...' > /opt/outputs/nodes.txt. Check the file with cat afterwards.
  • kubectl explain <resource>.<field> --recursive shows field paths when you don't have an object to dump.

[!WARNING] Exam traps / remember:

  • -o jsonpath starts at the root, so you need .items[*] (or {range .items[*]}) for lists. custom-columns and --sort-by work per item, so adding .items[*] there breaks them.
  • With a single object (kubectl get pod nginx -o jsonpath=...) there is no .items. The path starts at the object, e.g. '{.spec.containers[*].image}'.
  • {.items[*].a} {.items[*].b} prints all the a values, then all the b values. Only range ... {end} gives one row per object, and the {end} is easy to forget.
  • Escape characters go inside braces and quotes: {"\n"} and {"\t"}, never a bare \n.
  • Do not put spaces after the commas in a custom-columns spec. Column names come before the colon.

Trimmed kubectl get pods -o json, which is how the image path is derived:

{
  "apiVersion": "v1",
  "kind": "List",
  "items": [
    {
      "apiVersion": "v1",
      "kind": "Pod",
      "metadata": { "name": "nginx-5557945897-gznjp" },
      "spec": {
        "containers": [ { "image": "nginx:alpine", "name": "nginx" } ],
        "nodeName": "node01"
      }
    }
  ]
}

Path: .items[0].spec.containers[0].image → nginx:alpine.

[!NOTE] Beyond the course: default kubectl get tables are normally generated server-side (a Table API response). Explicit JSONPath/custom-columns output is evaluated client-side using resource objects.


← 14 Kustomize Basics (2025 Updates) · Index · 16 Mock Exams →


Reference

Based on the KodeKloud Certified Kubernetes Administrator (CKA) course notes.

On this page