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.
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.