✦ For everyone, free.

Practical knowledge for real and everyday life

Home

Kubernetes CRD Subresource Management

Kubernetes CRD Subresource Management allows custom resources to expose subresources, improving API flexibility and operational capabilities in Kubernetes.

Kubernetes CRD Subresource Management is the practice of configuring and correctly using the status and scale subresources on a Custom Resource Definition, which split a single logical object into multiple independently addressable API endpoints, each with its own RBAC surface and update semantics, in order to enforce clean separation between user-declared intent and controller-reported observation.


The Status Subresource

Splitting Spec Writes from Status Writes

spec:
  versions:
    - name: v1
      subresources:
        status: {}

Declaring the status subresource creates a distinct /apis/databases.example.com/v1/namespaces/{ns}/postgresclusters/{name}/status endpoint; ordinary PUT/PATCH requests against the main resource endpoint can no longer modify status at all, and conversely, requests against the /status endpoint can modify only status, never spec or metadata (aside from resourceVersion and labels/annotations under some clients).

kubectl get --raw "/apis/databases.example.com/v1/namespaces/production/postgresclusters/orders-db/status"

RBAC Isolation Enabled by the Subresource

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: postgrescluster-controller
rules:
  - apiGroups: ["databases.example.com"]
    resources: ["postgresclusters"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["databases.example.com"]
    resources: ["postgresclusters/status"]
    verbs: ["get", "update", "patch"]

This RBAC pattern grants a controller read-only access to user intent (spec) and write access only to status, meaning even a compromised or buggy controller cannot alter what a user declared, only report what it observed, a separation that is one of the primary security and correctness benefits of enabling the status subresource.

spec User Authority , status Controller Authority

Writing Status Correctly

Using UpdateStatus, Not Update

cluster.Status.ReadyReplicas = readyCount
err := r.Status().Update(ctx, &cluster)

Controller-runtime and equivalent client libraries expose a distinct Status() update path specifically because a plain Update() call against an object with the status subresource enabled silently ignores any change to the status field, a subtle bug that produces a controller which appears to run without error but never actually reports observed state.

Conditions as the Standard Status Pattern

status:
  conditions:
    - type: Available
      status: "True"
      reason: AllReplicasReady
      message: "3/3 replicas ready"
      lastTransitionTime: "2024-06-01T10:00:00Z"
      observedGeneration: 5

The Kubernetes-wide convention of a conditions array with type, status, reason, message, and lastTransitionTime gives generic tooling (dashboards, kubectl wait, alerting rules) a uniform way to interpret any custom resource's health without needing resource-specific parsing logic.

kubectl wait postgrescluster/orders-db --for=condition=Available --timeout=120s

The Scale Subresource

Mapping to the Standard Scale Contract

spec:
  versions:
    - name: v1
      subresources:
        scale:
          specReplicasPath: .spec.replicas
          statusReplicasPath: .status.readyReplicas
          labelSelectorPath: .status.labelSelector

The scale subresource maps arbitrary field paths in a custom resource onto the generic autoscaling/v1.Scale object shape, which is what allows the Horizontal Pod Autoscaler, kubectl scale, and any other scale-aware tooling to target a custom resource exactly as they would target a Deployment, without those tools needing any custom-resource-specific code.

kubectl scale postgrescluster orders-db --replicas=5

labelSelectorPath and HPA Compatibility

The labelSelectorPath field is required specifically for HPA compatibility, since the autoscaler uses the selector to count and evaluate metrics against the pods the custom resource is understood to own; omitting it leaves scale technically functional for direct kubectl scale calls but breaks HPA targeting.

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: orders-db-hpa
spec:
  scaleTargetRef:
    apiVersion: databases.example.com/v1
    kind: PostgresCluster
    name: orders-db
  minReplicas: 2
  maxReplicas: 8

Observability Through observedGeneration

Detecting Stale Status

metadata:
  generation: 6
status:
  observedGeneration: 5

Comparing metadata.generation (incremented by the API server on every spec change) against status.observedGeneration (set by the controller to the generation it last reconciled) lets any client detect whether the displayed status reflects the current spec or is stale because the controller has not yet caught up, a pattern essential for accurate dashboards and kubectl wait conditions during a rollout.


Relationship to CRD Spec Structure and the Extension Model

Subresource management is the practical implementation of the spec/status separation and level-triggered reconciliation principles introduced by the broader Kubernetes extension model, made concrete through the specific subresources field of CRD spec structure: correctly enabling and using status and scale is what allows a custom resource to participate fully in the same generic tooling, RBAC isolation, and autoscaling ecosystem that built-in Kubernetes resources enjoy by default.

PostgresCluster/orders-db /spec (user) /status (controller)