Last updated: April 28, 2026
Kubernetes Deployment YAML
Field-by-field anatomy of a Kubernetes Deployment manifest, plus the Service, ConfigMap, and Secret YAML you almost always need alongside it. Copy-ready Nginx example included.
Written by Mohan Raj Kolavi.
Quick answer
A Kubernetes Deployment YAML uses apiVersion: apps/v1, kind: Deployment, a metadata.name, and a spec with replicas, a selector, and a pod template defining containers and their image, ports, resources, env, and probes. Apply it with kubectl apply -f deployment.yaml.
Nginx Deployment example
A complete, working Deployment that runs three Nginx replicas with sensible CPU and memory bounds:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
Field-by-field:
apiVersion: apps/v1- the API group and version for Deployments.kind: Deployment- the resource type.metadata.name- unique within the namespace.spec.replicas: 3- desired pod count.spec.selector.matchLabels- must match the labels inspec.template.metadata.labels, or the Deployment will not own its pods.spec.template- the pod template, used to create each replica.resources.requests/limits- what the scheduler reserves and the maximum the container can consume.
Service manifest
A Deployment without a Service is unreachable from anywhere except the pod itself. The matching ClusterIP Service routes traffic to any pod with the app: nginx label:
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: ClusterIP
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
port is what other workloads in the cluster call. targetPort is the pod port traffic is forwarded to. Use type: NodePort or LoadBalancer to expose the service outside the cluster.
ConfigMap example
A ConfigMap stores non-secret configuration as key-value pairs, with optional embedded YAML or JSON files:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
LOG_LEVEL: info
FEATURE_FLAGS: "search,recommendations"
config.yaml: |
server:
port: 8080
timeout: 30s
The config.yaml entry uses a literal block scalar (|) to embed a multiline value. See the multiline strings guide for the full block-scalar reference.
Secret example
stringData takes plaintext that Kubernetes base64-encodes when it stores the Secret. Use this form rather than data (which expects values to already be base64) to keep the YAML readable:
apiVersion: v1 kind: Secret metadata: name: app-secrets type: Opaque stringData: DATABASE_URL: "postgres://user:pass@db:5432/app" API_KEY: "sk_live_xxxxxxxxxxxxx"
Do not commit raw Secret manifests to Git - base64 is encoding, not encryption. Use Sealed Secrets, External Secrets Operator, or SOPS to handle them safely.
Probes and environment injection
Production-grade Deployments wire env vars from a ConfigMap and Secret, and expose readiness and liveness probes so Kubernetes can stop sending traffic to broken pods:
# Same Deployment with health probes and env from ConfigMap + Secret
spec:
template:
spec:
containers:
- name: api
image: myorg/api:1.4.2
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: app-secrets
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
readinessProbe controls when traffic is routed to the pod. livenessProbe controls when the kubelet restarts the pod. Set both - they answer different questions.
Multiple resources in one file
YAML supports multiple documents in a single file separated by ---. This is how a single manifest ships a Namespace, Deployment, Service, and ConfigMap together:
# Multiple resources in one file - separated by ---
apiVersion: v1
kind: Namespace
metadata:
name: production
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: production
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.27
Applying with kubectl
kubectl apply -f file.yaml- create or update from a single file.kubectl apply -f .- apply every YAML in the directory.kubectl apply --dry-run=client -f file.yaml- validate without contacting the cluster.kubectl diff -f file.yaml- preview the change that apply would make.kubectl rollout status deployment/<name>- wait for the new replicas to become healthy.kubectl rollout undo deployment/<name>- roll back to the previous revision.
Common errors
- error validating data: unknown field "X" - typo in a field name, or wrong API version. Check the
apiVersionfirst. - spec.selector: required value - Deployments require
spec.selector.matchLabelsmatching the pod template's labels. - found character that cannot start any token - tabs in indentation. YAML rejects tabs; use two spaces. Run the file through the YAML validator to find the exact line.
- ImagePullBackOff - not a YAML problem; check the image name, tag, and pull secret.
- CrashLoopBackOff - container starts and exits. Inspect logs with
kubectl logs <pod>and check the readiness probe.
Validate and convert
Indentation bugs in Kubernetes YAML are silent until apply time. Drop your manifest into the YAML validator before running kubectl, or use the YAML formatter to normalize indentation across a repo. For shared blocks across multiple manifests, see YAML anchors and merge keys.
Frequently Asked Questions
What is a Kubernetes deployment YAML?
A Kubernetes deployment YAML is a manifest file (typically with a .yaml or .yml extension) that declares a Deployment resource. It tells the Kubernetes control plane which container image to run, how many replicas to maintain, the rolling-update strategy, and the pod template. Apply it with 'kubectl apply -f deployment.yaml'.
What are the four required fields in every Kubernetes manifest?
Every manifest must include 'apiVersion' (the Kubernetes API group and version), 'kind' (the resource type, e.g. Deployment, Service), 'metadata' (at minimum a name, plus optional labels and namespace), and 'spec' (the desired state for that kind of resource).
What is the apiVersion for a Kubernetes Deployment?
Use 'apps/v1'. The older 'extensions/v1beta1' and 'apps/v1beta1' versions are deprecated and removed in modern clusters. ConfigMap, Service, Secret, and Namespace use 'v1' (the core API group).
How many replicas should I set in a Kubernetes Deployment?
Start with three for production-grade web services - that survives a single node failure and allows zero-downtime rolling updates. Use one for stateful or singleton workloads. The replicas field can be changed later with 'kubectl scale' or by editing the YAML and re-applying.
How do I apply a Kubernetes YAML file?
Run 'kubectl apply -f deployment.yaml' to create or update the resources defined in the file. 'kubectl apply -f .' applies every YAML in the current directory. Use 'kubectl diff -f file.yaml' to preview the change before applying.
What is the difference between 'kubectl apply' and 'kubectl create'?
'kubectl create' fails if the resource already exists. 'kubectl apply' creates the resource if absent and patches it if present, using a three-way merge between the YAML, the live object, and the last-applied annotation. Apply is the recommended choice for almost all GitOps and declarative workflows.
How do I pass environment variables to a Kubernetes container?
Three options: hardcode under spec.template.spec.containers[].env (each entry has a name and value), reference a ConfigMap or Secret per-key with valueFrom.configMapKeyRef or valueFrom.secretKeyRef, or pull every key from a ConfigMap/Secret with envFrom.configMapRef or envFrom.secretRef.
How do I set CPU and memory limits in a Deployment YAML?
Add a resources block under each container. 'requests' is what the scheduler reserves; 'limits' is the maximum the container can use. CPU is measured in cores or millicores ('500m' = 0.5 CPU). Memory uses Mi (mebibytes) or Gi (gibibytes). Always set both requests and limits in production.
How do I put multiple Kubernetes resources in one YAML file?
Separate documents with a line containing only three dashes (---). Each document is parsed as a complete manifest. 'kubectl apply -f file.yaml' processes every document in order. This is the standard way to ship a Deployment plus its Service plus its ConfigMap as a single artifact.
Why does my Kubernetes YAML get rejected with 'error validating data'?
The most common causes are wrong apiVersion or kind, indentation issues (always two spaces, never tabs), unknown fields under spec, missing required fields like 'selector' on a Deployment, or quoting numeric strings incorrectly. Run 'kubectl apply --dry-run=client -f file.yaml' to validate before applying for real.
Should I store Kubernetes Secrets as YAML in Git?
Plain Secret manifests are base64-encoded, not encrypted - committing them is equivalent to committing plaintext. Use Sealed Secrets, External Secrets Operator, SOPS-encrypted YAML, or an external secret manager (Vault, AWS Secrets Manager) and reference it from your deployment instead.
What is the difference between containerPort, port, and targetPort?
'containerPort' is the port the container listens on inside the pod. 'port' is the port the Service exposes inside the cluster. 'targetPort' is the pod port the Service forwards to (defaults to 'port' if omitted, but should match the container's containerPort). Get all three right or traffic does not reach the pod.