Skip to main content
RunBook Academy

KubernetesV · Kubernetes Objects and MetadataKubernetes objects and metadata

Kubernetes objects — apiVersion, kind, metadata

Intermediate⏱ ~16 minkubectl

What you'll learn

  • Identify the top-level fields of every Kubernetes object: apiVersion, kind, metadata, spec, status
  • Explain how apiVersion and kind select the API endpoint and schema
  • Distinguish cluster-scoped from resources namespaced objects
  • Use `kubectl explain` and the OpenAPI schema to discover object fields

Prerequisites

Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16

Not yet marked complete on this device.

Every Kubernetes object — Pod, Deployment, Service, ConfigMap, Secret, custom CRD — has the same top-level structure. This lesson dissects the fields every object shares, explains what each one does, and shows how to discover fields you don’t know.

The top-level shape

apiVersion: apps/v1                # which API group/version
kind: Deployment                   # which resource type
metadata:                          # identifying information
  name: web
  namespace: prod
  uid: 7c8f2d8e-...
  resourceVersion: "128034"
  generation: 3
  creationTimestamp: "2026-08-15T12:01:01Z"
  labels:
    app: web
    env: prod
  annotations:
    description: "Production web tier"
spec:                              # desired state (controller reads this)
  replicas: 5
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: nginx
        image: nginx:1.27.1
status:                            # observed state (controller writes this)
  replicas: 5
  readyReplicas: 5
  conditions:
  - type: Progressing
    status: "True"

Five top-level fields:

  • apiVersion — which API group and version
  • kind — which resource type
  • metadata — name, namespace, uid, labels, annotations, etc.
  • spec — desired state (controller reads)
  • status — observed state (controller writes)

apiVersion and kind are required. metadata is required. spec is required for objects with intent (most kinds) but absent for some (e.g., Namespace, Node). status is optional from the operator’s side; the controller writes it.

apiVersion

apiVersion identifies the API group and version:

apiVersion: v1                     # core group, version v1
apiVersion: apps/v1                # apps group, version v1
apiVersion: batch/v1               # batch group, version v1
apiVersion: networking.k8s.io/v1   # networking group, version v1
apiVersion: rbac.authorization.k8s.io/v1
apiVersion: storage.k8s.io/v1
apiVersion: policy/v1

The version follows semver-like semantics. Within a group:

  • v1 is the current stable version
  • v1beta1 is a deprecated beta (typically available for 9 months, then removed)
  • v1alpha1 is an experimental version

The API server may serve multiple versions simultaneously (e.g., apps/v1 and apps/v1beta1), with conversion between them. Storage happens at the internal version; conversion is applied on read/write.

flowchart LR
    API[API server] -->|read with apiVersion=apps/v1beta1| C[Convert]
    C -->|internal apps/v1| ETCD[etcd]
    C -->|apps/v1 representation| Client[Client]

When a version is deprecated, manifests referencing it still work but produce deprecation warnings. Removing a version is a breaking change for any workload referencing it; see Part LXXIX for upgrade considerations.

kind

kind is the resource type within the API group:

kind: Pod
kind: Deployment
kind: Service
kind: ConfigMap
kind: Secret
kind: StatefulSet
kind: Job
kind: CronJob

The combination of apiVersion and kind uniquely identifies a schema. The OpenAPI schema is at /openapi/v2 and /openapi/v3 on the API server.

The API server’s storage path uses kind:

/registry/<group>/<version>/<kind>/<namespace>/<name>

For example, a Deployment in prod:

/registry/apps/v1/deployments/prod/web

metadata

metadata holds identifying information. Common fields:

name

The name of the object within its namespace (or cluster-wide for cluster-scoped objects).

metadata:
  name: web

Names must be DNS-1123 compliant: lowercase, alphanumerics, hyphens, dots (with caveats). The maximum length is 253 characters (for most kinds).

namespace

The namespace for namespaced objects. Cluster-scoped objects (Node, Namespace, PersistentVolume, ClusterRole) cannot have a namespace.

metadata:
  namespace: prod

If metadata.namespace is empty for a namespaced object, it defaults to default — almost always a production mistake (see Part XIV).

uid

A unique identifier assigned by the API server on creation. Immutable. Used internally for ownership tracking.

metadata:
  uid: 7c8f2d8e-b2e1-4f6a-9c8d-1a2b3c4d5e6f

resourceVersion

The etcd revision at which the object was last written. Used for optimistic concurrency (see Part III).

metadata:
  resourceVersion: "128034"

generation

A monotonically increasing counter that increments on every spec change. Used by controllers to track “which spec version has been reconciled”.

metadata:
  generation: 3
status:
  observedGeneration: 3

A controller writes status.observedGeneration = metadata.generation after acting. The difference between them indicates spec changes the controller has not yet processed.

creationTimestamp, deletionTimestamp

creationTimestamp is set by the API server when the object is created.

deletionTimestamp is set when the object is marked for deletion. The object is not actually deleted until all finalizers have been processed.

labels

Key-value pairs used for selection and grouping. See kubernetes-v-03 for the full reference.

annotations

Key-value pairs for non-identifying metadata. See kubernetes-v-03.

ownerReferences

List of objects that own this one. Used by the garbage collector for cascading delete. See kubernetes-v-05.

finalizers

Strings that block deletion until removed. Used by controllers to perform cleanup. See kubernetes-v-06.

managedFields

Server-side apply metadata: which fields are managed by which actor. See kubernetes-v-02.

spec

The desired state the operator declares. The controller reads this.

spec:
  replicas: 5
  template:
    spec:
      containers:
      - name: web
        image: nginx:1.27.1

Different kinds have different spec shapes:

  • Pod.spec — containers, volumes, scheduling
  • Deployment.spec — replicas, selector, template, strategy
  • Service.spec — selector, ports, type
  • ConfigMap — has no spec; the data is in metadata or in data/binaryData

status

The observed state the controller writes. The operator usually does not write status.

status:
  replicas: 5
  readyReplicas: 5
  conditions:
  - type: Available
    status: "True"
    reason: MinimumReplicasAvailable
    message: "Deployment has minimum availability."

Different kinds have different status shapes:

  • Pod.status — phase, container statuses, pod IP, conditions
  • Deployment.status — replicas, readyReplicas, conditions
  • Node.status — addresses, conditions, capacity
  • PersistentVolume.status — phase, message

The contract:

  • Operators write spec.
  • Controllers write status.
  • spec.replicas = 5 and status.replicas = 5 means converged.
  • spec.replicas = 5 and status.replicas = 3 means the controller is working on it.

Cluster-scoped vs namespaced

flowchart LR
    Cluster[Cluster] --> ClusterScoped["Cluster-scoped<br/>Node, Namespace,<br/>PersistentVolume,<br/>ClusterRole"]
    Cluster --> Namespace[Namespace]
    Namespace --> Namespaced["Namespaced<br/>Pod, Deployment, Service,<br/>ConfigMap, Secret"]

Cluster-scoped objects have no metadata.namespace field (it would be rejected). Namespaced objects have metadata.namespace required.

kubectl api-resources --namespaced=true
kubectl api-resources --namespaced=false

How to discover fields

# Substitute your own values before running:
KIND=deployment
FIELD=spec
SUBFIELD=strategy

kubectl explain "$KIND"
kubectl explain "$KIND"."$FIELD"
kubectl explain "$KIND"."$FIELD"."$SUBFIELD"
$ kubectl explain deployment.spec.strategy
RESOURCE: strategy <Object>
DESCRIPTION:
    Deployment strategy to use to replace existing pods with new ones.

FIELDS:
  rollingUpdate    <Object>
    Rolling update params.
    maxSurge       <string> -required-
    maxUnavailable <string> -required-
  type             <string> -required-
    Type of deployment. Can be "Recreate" or "RollingUpdate".

The OpenAPI schema is at:

kubectl get --raw /openapi/v2 | jq '.definitions["io.k8s.api.apps.v1.Deployment"]'

For programmatic access, use kubectl get --raw /openapi/v3.

How objects are addressed

The combination of:

  • API group + version + kind
  • namespace + name
  • (optional) subresource

…addresses a unique object. The URL pattern:

/apis/<group>/<version>/namespaces/<namespace>/<kind>/<name>
/apis/<group>/<version>/<kind>/<name>          # cluster-scoped
/apis/<group>/<version>/namespaces/<namespace>/<kind>/<name>/status   # subresource

The API server enforces:

  • Exactly one of each name within a namespace
  • Spec validity against the schema
  • Optional subresource (e.g., /status for updates that only change status)

Cross-course references

  • The Docker course part XXVIII-Docker-Images covers image metadata that maps onto Kubernetes metadata patterns.
  • The Linux course part XXVI-Linux-SSH covers credential management primitives that map onto Kubernetes authentication for object writes.
  • The Observability course part IX-Observability-Exporters covers the metrics the API server exposes about object counts and operations.
  • The Docker course part XXXVII-Docker-Registries covers registry patterns that map onto cluster-wide object registries.

Quiz

Knowledge check · 4 questions

  1. Q1. Which five top-level fields are common to most Kubernetes objects?

  2. Q2. A `Node` object can have `metadata.namespace` set to `prod` to logically group it with prod workloads.

  3. Q3. An operator applies a Deployment manifest and gets `error: error validating "deployment.yaml": error validating data: ValidationError(Deployment.spec): unknown field "replcas"; if you choose to make these validation errors strict, then set the --strict flag ...`. Diagnose and remediate.

    Manifest: ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: web spec: replcas: 5 # typo selector: matchLabels: app: web template: metadata: labels: app: web spec: containers: - name: nginx image: nginx:1.27.1 ``` Error: ``` error: error validating "deployment.yaml": error validating data: ValidationError(Deployment.spec): unknown field "replcas"; if you choose to make these validation errors strict, then set the --strict flag ```

  4. Q4. Explain the relationship between `spec` and `status` on a Kubernetes object. Who writes each, and what does it mean when they agree?

Passing score: 75%. Answers are checked in this browser.

Production discipline

  • Treat apiVersion and kind as the object’s identity at the API server. Changing them creates a new object.
  • Treat metadata.name and metadata.namespace as immutable from the operator’s perspective (the API server will reject some changes; kubectl may create a new object in others).
  • Read status.conditions before assuming convergence. The controller’s assessment is the source of truth.
  • Pin apiVersion in production manifests; do not rely on auto-conversion between versions. Version changes are breaking for some fields.
  • Use kubectl explain to discover fields; do not guess or copy from outdated examples.