Writing Kubernetes Manifests in YAML
Kubernetes uses YAML as its primary configuration language for defining resources—deployments, services, config maps, and every other object in the cluster. While the API is well-documented, writing correct manifests requires understanding the structural conventions, required fields, and common patterns that make configs maintainable across environments. This guide covers the practical aspects of authoring Kubernetes YAML that works reliably in production.
Required Fields in Every Manifest
Every Kubernetes resource must include four top-level fields:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
labels:
app: api-server
spec:
# Resource-specific configuration
- apiVersion: The API group and version. Core resources use
v1; most workload resources useapps/v1; CRDs use their own group paths. - kind: The resource type. Must match an API type registered in your cluster.
- metadata: At minimum, a
name. Labels and annotations are strongly recommended. - spec: The desired state. The structure depends entirely on the resource
kind.
Omitting any of these fields produces a validation error at apply time. The metadata.name must be unique within its namespace for most resource types.
Deployment Manifest Pattern
The Deployment is the most common workload resource. A production-grade deployment includes replicas, update strategy, resource limits, health checks, and environment configuration:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
labels:
app: api-server
spec:
replicas: 3
selector:
matchLabels:
app: api-server
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
template:
metadata:
labels:
app: api-server
spec:
containers:
- name: api
image: registry.example.com/api:v1.2.0
ports:
- containerPort: 3000
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
livenessProbe:
httpGet:
path: /healthz
port: 3000
initialDelaySeconds: 10
periodSeconds: 15
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
env:
- name: NODE_ENV
value: production
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: api-secrets
key: database-url
Key conventions to follow:
- Always set resource requests and limits. Without limits, a single pod can consume all node resources. Without requests, the scheduler cannot make informed placement decisions.
- Always define both liveness and readiness probes. Liveness detects deadlocked processes; readiness controls traffic routing during startup and deployments.
- Use
valueFromfor secrets. Never hardcode sensitive values directly in the manifest.
Service and Ingress Pairing
A Deployment creates pods, but pods are not directly addressable from outside the cluster. You need a Service to provide a stable endpoint and an Ingress to expose it:
apiVersion: v1
kind: Service
metadata:
name: api-server
spec:
selector:
app: api-server
ports:
- port: 80
targetPort: 3000
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-server
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts:
- api.example.com
secretName: api-tls
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-server
port:
number: 80
The --- separator lets you define multiple resources in one file—a common convention for resources that are always deployed together.
Best Practices
Pin image tags. Avoid :latest in production—it makes rollbacks impossible and creates reproducibility issues. Use semantic version tags or SHA digests for maximum certainty.
Use namespaces logically. Group resources by environment (production, staging) or by team. Never put all workloads in default.
Label consistently. Define a labeling convention (app, version, team, environment) and apply it across all resources. Labels are the primary mechanism for selection and filtering.
Validate before applying. Use kubectl apply --dry-run=client or tools like kubeval and kubeconform to validate manifests against the Kubernetes API schema before pushing to the cluster.
Store manifests in Git. GitOps tools like ArgoCD and Flux watch a Git repository and apply changes automatically. Storing manifests in version control provides audit trails and rollback capability.
For converting between JSON and YAML formats when working with Kubernetes configs, the JSON-YAML Converter tool handles expansion and validation of multi-document files.