Skip to content

Manifests

First PublishedByAtif Alam

Everything in Kubernetes is defined as a manifest — a YAML (or JSON) file that describes the desired state of a resource. You apply manifests with kubectl apply -f <file>.

Every manifest has these fields:

apiVersion: apps/v1 # API group and version
kind: Deployment # object type
metadata: # identity and labels
name: my-app
namespace: default
labels:
app: my-app
spec: # desired state (varies by kind)
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: nginx:1.25

Which API group and version to use. Common values:

apiVersionUsed For
v1Pod, Service, ConfigMap, Secret, Namespace, PVC
apps/v1Deployment, StatefulSet, DaemonSet, ReplicaSet
batch/v1Job, CronJob
networking.k8s.io/v1Ingress, NetworkPolicy
autoscaling/v2HorizontalPodAutoscaler
policy/v1PodDisruptionBudget

The type of object: Pod, Deployment, Service, Ingress, ConfigMap, Secret, etc. Must match the apiVersion (e.g. Deployment requires apps/v1).

Identity and organizational fields:

  • name — Required. Unique within the namespace for that kind.
  • namespace — Optional (defaults to default). Not all resources are namespaced (e.g. Nodes, PersistentVolumes are cluster-scoped).
  • labels — Key-value pairs for grouping and selecting (e.g. app: my-app, env: production). Used by Services, Deployments, and kubectl -l.
  • annotations — Key-value pairs for non-identifying metadata (e.g. build info, tool config, descriptions). Not used for selection; purely informational or for tooling.
metadata:
name: my-app
namespace: staging
labels:
app: my-app
env: staging
annotations:
description: "Frontend web server"
managed-by: "helm"

The desired state — what you want Kubernetes to do. Structure varies by kind:

  • For a Deployment: replicas, selector, pod template.
  • For a Service: selector, ports, type.
  • For a PVC: access modes, storage size, storage class.

When you apply a manifest, Kubernetes stores two things:

  • spec — What you declared (your desired state). You write this.
  • status — What actually exists (current state). Kubernetes writes this.

Controllers continuously reconcile: if status doesn’t match spec, they take action (create pods, restart containers, etc.).

You can see both with:

Terminal window
kubectl get deployment my-app -o yaml

The status section shows current replicas, conditions, and observed generation. You never write status in your manifests.

You can put multiple resources in one file, separated by ---:

apiVersion: v1
kind: Service
metadata:
name: my-app
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app:v1

You can also apply an entire directory:

Terminal window
kubectl apply -f ./k8s/ # applies all YAML files in the directory
Terminal window
kubectl apply -f manifest.yaml # create or update
kubectl apply -f manifest.yaml --dry-run=client # preview without applying
kubectl diff -f manifest.yaml # show what would change
kubectl delete -f manifest.yaml # delete resources defined in file
  • Declarative (recommended): Write manifests, run kubectl apply. Kubernetes converges toward the desired state. Easy to version-control and review.
  • Imperative: Run kubectl create, kubectl run, kubectl expose directly. Quick for one-offs and debugging, but hard to reproduce.

You don’t have to write manifests from scratch:

Terminal window
kubectl create deployment my-app --image=nginx --dry-run=client -o yaml > deployment.yaml
kubectl create service clusterip my-app --tcp=80:8080 --dry-run=client -o yaml > service.yaml

This generates a valid manifest you can edit and then apply.

Terminal window
kubectl apply -f manifest.yaml --dry-run=server # server-side validation
kubectl explain deployment.spec.template # docs for any field
kubectl explain pod.spec.containers # nested fields

kubectl explain is useful for looking up fields without leaving the terminal.

  • Every manifest has four fields: apiVersion, kind, metadata, spec.
  • Labels are for selection and grouping; annotations are for metadata and tooling.
  • spec = what you want; status = what exists. Controllers reconcile the gap.
  • Use multi-resource files or directories to keep related resources together.
  • Prefer declarative (kubectl apply -f) over imperative commands for anything you want to reproduce.
  • Use --dry-run=client -o yaml to generate manifests; kubectl explain to look up fields.