X Xerobit

YAML in Kubernetes — Writing Kubernetes Manifests

Kubernetes configurations are YAML files called manifests. Here's how Kubernetes YAML is structured, the required fields for common resources (Deployment, Service, ConfigMap),...

Mian Ali Khalid · · 6 min read
Use the tool
YAML ↔ JSON Converter
Convert between YAML and JSON formats with full fidelity.
Open YAML ↔ JSON Converter →

Kubernetes uses YAML for all resource definitions — called manifests. Every Kubernetes object (Pod, Deployment, Service, ConfigMap) is described as a YAML file and applied with kubectl apply -f.

Use the YAML to JSON Converter to validate your Kubernetes YAML before applying.

Required fields in every Kubernetes manifest

All Kubernetes resources share four required top-level fields:

apiVersion: apps/v1       # API group and version
kind: Deployment          # resource type
metadata:
  name: my-app            # resource name (must be unique in namespace)
  namespace: default      # optional, defaults to "default"
spec:                     # resource-specific configuration
  # ...

Deployment manifest

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server
  namespace: production
  labels:
    app: api-server
    team: backend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api-server     # must match template labels
  template:
    metadata:
      labels:
        app: api-server   # must match selector
    spec:
      containers:
        - name: api
          image: myregistry/api:v1.2.3
          ports:
            - containerPort: 3000
          env:
            - name: NODE_ENV
              value: production
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: db-secrets
                  key: url
          resources:
            requests:
              cpu: 100m      # 0.1 CPU core
              memory: 128Mi  # 128 megabytes
            limits:
              cpu: 500m
              memory: 512Mi
          readinessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 15
            periodSeconds: 30

Service manifest

apiVersion: v1
kind: Service
metadata:
  name: api-server
  namespace: production
spec:
  selector:
    app: api-server       # routes traffic to pods with this label
  ports:
    - protocol: TCP
      port: 80            # service port (cluster-internal)
      targetPort: 3000    # container port
  type: ClusterIP         # internal only (default)

Service types:

  • ClusterIP — internal cluster access only
  • NodePort — expose on each node’s IP at a static port
  • LoadBalancer — provision a cloud load balancer
  • ExternalName — map to an external DNS name

ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: production
data:
  # Key-value pairs:
  NODE_ENV: production
  LOG_LEVEL: info
  
  # Multi-line value (for config files):
  nginx.conf: |
    server {
      listen 80;
      location / {
        proxy_pass http://backend:3000;
      }
    }
  
  app-config.json: |
    {
      "featureFlags": {
        "newUI": true,
        "betaFeatures": false
      }
    }

Secret manifest

apiVersion: v1
kind: Secret
metadata:
  name: db-secrets
  namespace: production
type: Opaque
data:
  # Values must be base64 encoded:
  username: dXNlcm5hbWU=   # echo -n 'username' | base64
  password: cGFzc3dvcmQ=   # echo -n 'password' | base64
  url: cG9zdGdyZXNxbDovLy4uLg==

Referencing a secret in a Deployment:

env:
  - name: DATABASE_URL
    valueFrom:
      secretKeyRef:
        name: db-secrets
        key: url

Multi-document YAML files

Use --- to separate multiple resources in one file:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 2
  # ...

---
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  selector:
    app: web
  ports:
    - port: 80

Apply all at once: kubectl apply -f all-resources.yaml

Common YAML mistakes in Kubernetes

Indentation errors

# WRONG: selector must be under spec
apiVersion: apps/v1
kind: Deployment
spec:
selector:         # indentation error!
  matchLabels:
    app: web

# CORRECT:
apiVersion: apps/v1
kind: Deployment
spec:
  selector:
    matchLabels:
      app: web

String vs integer ports

# YAML might parse port as integer or string:
containerPort: 3000   # integer — correct
containerPort: "3000" # string — may cause errors in some fields

Label mismatches

# WRONG: selector doesn't match template labels
spec:
  selector:
    matchLabels:
      app: web          # looks for this label
  template:
    metadata:
      labels:
        app: website    # different label!

The pod template’s labels must include all labels specified in selector.matchLabels.

Applying and managing manifests

# Apply (create or update):
kubectl apply -f deployment.yaml
kubectl apply -f ./k8s/   # apply all YAML in directory

# Check what would change (dry run):
kubectl apply -f deployment.yaml --dry-run=client

# Delete:
kubectl delete -f deployment.yaml

# View current config:
kubectl get deployment api-server -o yaml

# Diff current vs file:
kubectl diff -f deployment.yaml

Related posts

Related tool

YAML ↔ JSON Converter

Convert between YAML and JSON formats with full fidelity.

Written by Mian Ali Khalid. Part of the Data & Format pillar.