KubernetesV · Kubernetes Objects and MetadataKubernetes objects and metadata
Kubernetes objects — apiVersion, kind, metadata
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
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 versionkind— which resource typemetadata— 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:
v1is the current stable versionv1beta1is a deprecated beta (typically available for 9 months, then removed)v1alpha1is 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, schedulingDeployment.spec— replicas, selector, template, strategyService.spec— selector, ports, typeConfigMap— has nospec; the data is inmetadataor indata/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, conditionsDeployment.status— replicas, readyReplicas, conditionsNode.status— addresses, conditions, capacityPersistentVolume.status— phase, message
The contract:
- Operators write
spec. - Controllers write
status. spec.replicas = 5andstatus.replicas = 5means converged.spec.replicas = 5andstatus.replicas = 3means 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.,
/statusfor updates that only change status)
Cross-course references
- The Docker course part
XXVIII-Docker-Imagescovers image metadata that maps onto Kubernetesmetadatapatterns. - The Linux course part
XXVI-Linux-SSHcovers credential management primitives that map onto Kubernetes authentication for object writes. - The Observability course part
IX-Observability-Exporterscovers the metrics the API server exposes about object counts and operations. - The Docker course part
XXXVII-Docker-Registriescovers registry patterns that map onto cluster-wide object registries.
Quiz
Knowledge check · 4 questions
Q1. Which five top-level fields are common to most Kubernetes objects?
Q2. A `Node` object can have `metadata.namespace` set to `prod` to logically group it with prod workloads.
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 ```
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
apiVersionandkindas the object’s identity at the API server. Changing them creates a new object. - Treat
metadata.nameandmetadata.namespaceas immutable from the operator’s perspective (the API server will reject some changes; kubectl may create a new object in others). - Read
status.conditionsbefore assuming convergence. The controller’s assessment is the source of truth. - Pin
apiVersionin production manifests; do not rely on auto-conversion between versions. Version changes are breaking for some fields. - Use
kubectl explainto discover fields; do not guess or copy from outdated examples.