✦ For everyone, free.

Practical knowledge for real and everyday life

Home

Kubernetes CRD Printer Column Management

Kubernetes CRD Printer Column Management defines how custom resource data is structured and displayed in terminal output.

Kubernetes CRD Printer Column Management is the practice of designing which fields of a custom resource appear as columns in kubectl get output by default, through the additionalPrinterColumns field of a CRD's version spec, shaping the resource's day-to-day operational usability without requiring every user to fall back to -o yaml or -o json to see meaningful state.


Declaring Printer Columns

Basic Column Definition

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: postgresclusters.databases.example.com
spec:
  versions:
    - name: v1
      additionalPrinterColumns:
        - name: Replicas
          type: integer
          jsonPath: .spec.replicas
        - name: Ready
          type: string
          jsonPath: .status.conditions[?(@.type=="Available")].status
        - name: Age
          type: date
          jsonPath: .metadata.creationTimestamp

Each column entry specifies a display name, a type matching the OpenAPI type of the underlying field (string, integer, boolean, date, or number), and a jsonPath expression selecting the value to display, evaluated against the object's own JSON representation.

kubectl get postgresclusters
NAME         REPLICAS   READY   AGE
orders-db    3          True    5d

The Implicit Age Column

kubectl get always includes an Age column derived from metadata.creationTimestamp for any resource by default; declaring it explicitly under additionalPrinterColumns is only necessary if its position among other custom columns needs to be controlled, since custom columns are otherwise appended after the implicit ones in declaration order.


Choosing Which Fields to Surface

Prioritizing Operational Signal

The design principle behind printer column selection mirrors dashboard design: the two or three fields an operator checks most frequently during routine inspection — readiness, phase, a key scale or size figure — belong as printer columns, while less frequently needed detail remains available only through -o yaml, since printer columns are a fixed-width table and lose value if overloaded with too many fields to remain scannable.

additionalPrinterColumns:
  - name: Phase
    type: string
    jsonPath: .status.phase
  - name: Storage
    type: string
    jsonPath: .spec.storageSize
  - name: Age
    type: date
    jsonPath: .metadata.creationTimestamp

Priority Columns for Wide Output

additionalPrinterColumns:
  - name: Version
    type: string
    jsonPath: .status.postgresVersion
    priority: 1

Setting priority: 1 on a column marks it as shown only under kubectl get -o wide rather than in the default table, allowing a resource to expose more detail to users who explicitly request it without cluttering the default listing seen by everyone.

Visible by default = priority = 0

JSONPath Expression Practice

Selecting Nested and Conditional Fields

jsonPath: .status.conditions[?(@.type=="Available")].status

Because status.conditions is an array, extracting a specific condition's status requires a JSONPath filter expression matching on type, a pattern that recurs across nearly every printer column referencing the standard Kubernetes conditions convention rather than a flat status field.

Handling Missing Values Gracefully

A jsonPath referencing a field that does not yet exist on a given object (for instance, before the controller has written any status at all) renders as <none> in kubectl get output rather than producing an error, which is expected behavior during the interval between object creation and first reconciliation.

kubectl get postgresclusters
NAME         REPLICAS   READY     AGE
new-db       3          <none>    12s

Per-Version Column Consistency

Columns Are Declared Per Version

spec:
  versions:
    - name: v1alpha1
      additionalPrinterColumns:
        - name: Replicas
          type: integer
          jsonPath: .spec.replicas
    - name: v1
      additionalPrinterColumns:
        - name: Replicas
          type: integer
          jsonPath: .spec.replicas
        - name: Ready
          type: string
          jsonPath: .status.conditions[?(@.type=="Available")].status

Because printer columns are configured independently for each served version, a schema change that renames or restructures a field (moving spec.replicaCount to spec.replicas in a new version, for instance) requires updating the corresponding jsonPath in that version's printer columns as well, or the new version's kubectl get output silently shows <none> for a field that in fact exists under a different path.


Relationship to CRD Spec Structure and Subresource Management

Printer column management operates on the same per-version configuration surface described in CRD spec structure, and it frequently surfaces exactly the fields exposed through the status subresource — conditions, replica counts, phase — making it the final, user-facing presentation layer for the observability data a controller works to keep accurate: a correctly implemented status subresource with no corresponding printer columns is fully functional but invisible in routine kubectl get usage, while thoughtfully chosen printer columns are what make that status data immediately useful in practice.

PostgresCluster spec.replicas: 3 status.conditions[0] kubectl get table REPLICAS: 3 READY: True