How to Write a Kubernetes Deployment YAML (Production-Ready)
A production Kubernetes Deployment YAML is more than 'image + replicas.' It needs a rolling-update strategy that prevents downtime, resource limits that protect neighbors, and a readiness probe that keeps bad pods out of traffic. Here's the full pattern.
The minimal production Deployment
A toy Deployment works in a demo. A production Deployment guarantees zero-downtime rollouts and prevents one container from starving its neighbors. The key fields that make that difference:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 2
selector:
matchLabels:
app: api
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0 # never take a pod down before a new one is Ready
maxSurge: 1 # spin up one extra pod during the rollout
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: busybox:1.36
command: ["sleep", "86400"]
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
readinessProbe:
exec:
command: ["/bin/true"]
initialDelaySeconds: 5
Why maxUnavailable: 0 matters
The default maxUnavailable is 25%, which means Kubernetes can kill a running pod before
its replacement is ready. Setting maxUnavailable: 0 forces a "surge before remove" order:
Kubernetes brings a new pod to Ready first, then terminates the old one. With two replicas
that means your service never drops below full capacity during a rollout.
Requests vs limits
- requests - what the scheduler reserves on the node. A pod is only placed where this much CPU/memory is free.
- limits - the hard ceiling. Hit the memory limit and the container is OOMKilled; hit the CPU limit and it is throttled. Omitting limits lets one runaway container starve the whole node.
Matching ClusterIP Service
A Deployment is unreachable without a Service. The selector must match the pod labels exactly:
apiVersion: v1
kind: Service
metadata:
name: api
spec:
type: ClusterIP
selector:
app: api # must match spec.template.metadata.labels
ports:
- port: 80
targetPort: 80
ClusterIP is the default - it gives the Service a stable in-cluster IP. Nothing outside
the cluster can reach it, which is the right default for an internal API.
Verify the rollout
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl rollout status deployment/api # blocks until all pods are Ready
kubectl get pods,svc # confirm 2/2 Running + ClusterIP assigned
rollout status returns exit code 0 on success - useful in CI pipelines to gate a deploy.
Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.
Try it in a workspace →What you'll practice
- Writing a Deployment with a RollingUpdate strategy (maxUnavailable: 0, maxSurge: 1)
- Setting resource requests and limits to protect node stability
- Wiring a ClusterIP Service to a Deployment with a matching selector
FAQ
What does maxUnavailable: 0 do in a Kubernetes Deployment?
It prevents Kubernetes from terminating any existing pod until a replacement is fully Ready. Combined with maxSurge: 1, this gives you a true zero-downtime rolling update - the new pod must pass its readiness probe before the old one is removed.
What is the difference between resource requests and limits in Kubernetes?
Requests are what the scheduler reserves - a pod only lands on a node that has at least that much free. Limits are the hard ceiling at runtime: exceed the memory limit and the container is OOMKilled; exceed the CPU limit and it is throttled. Always set both.
Why does a Kubernetes Deployment need a matching Service?
A Deployment manages pods, but pods get random IPs that change on restart. A Service gives a stable ClusterIP and DNS name that routes to any Ready pod matching the selector. Without it, nothing else in the cluster can reliably reach the Deployment.
What is a Deployment YAML file in Kubernetes?
A Deployment YAML declares the desired state for a set of pods: the image to run, replica count, update strategy, and probes. Kubernetes continuously reconciles the cluster to match it.
Keep learning
Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.
Open the workspace →