Containers
Kubernetes Deployments: A Practical Introduction
Build and operate a Kubernetes Deployment with matching selectors, health checks, resource requests, rolling updates, verification, and rollback.
Dikshant Rai · · 8 min read
A container image is not a deployment plan. Kubernetes still needs to know how many copies to run, how to identify them, when a new copy is ready for traffic, and how to replace an old version. A Deployment captures that desired state for a stateless application and continuously reconciles the cluster toward it.
This guide creates one Deployment, observes the objects it controls, performs a rolling update, and exercises rollback. The emphasis is on the fields that affect production behavior, not on producing the shortest possible YAML.
What a Deployment controls
A Deployment manages ReplicaSets, and each ReplicaSet manages Pods. You normally declare and update the Deployment; its controller creates the lower-level objects.
Deployment
-> ReplicaSet for the current Pod template
-> Pod
-> Pod
-> Pod
The Deployment records a desired Pod template and replica count. When the template changes, Kubernetes creates a new ReplicaSet and gradually shifts replicas to it according to the update strategy. Old ReplicaSets are retained within the configured revision history so a rollout can be reversed.
Use a Deployment for replaceable application replicas that do not require stable Pod identity. StatefulSets, DaemonSets, Jobs, and CronJobs solve different workload problems; changing the kind later is more disruptive than choosing it deliberately at the start.
Check the target before applying
kubectl acts on the current kubeconfig context and namespace. Confirm both before a write:
kubectl config current-context
kubectl config view --minify --output 'jsonpath={..namespace}'
kubectl auth can-i create deployments.apps --namespace applications
An empty namespace output means default, not that no namespace is selected. In shared environments, pass --namespace explicitly in operational commands and keep the namespace in the manifest.
A deployment manifest with operational defaults
The following manifest runs three replicas of an HTTP service. Replace the example image with an immutable image digest or a version tag from your own registry before applying it:
apiVersion: apps/v1
kind: Deployment
metadata:
name: release-api
namespace: applications
labels:
app.kubernetes.io/name: release-api
spec:
replicas: 3
revisionHistoryLimit: 5
progressDeadlineSeconds: 300
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
selector:
matchLabels:
app.kubernetes.io/name: release-api
template:
metadata:
labels:
app.kubernetes.io/name: release-api
spec:
containers:
- name: api
image: ghcr.io/example/release-api:1.0.0
ports:
- name: http
containerPort: 8080
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
memory: 256Mi
readinessProbe:
httpGet:
path: /ready
port: http
initialDelaySeconds: 3
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 3
livenessProbe:
httpGet:
path: /live
port: http
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 2
Several details carry more weight than their line count suggests.
The selector under spec.selector must match the labels in spec.template.metadata.labels. A Deployment uses that selector to decide which Pods it owns. In apps/v1, the selector is required and effectively immutable after creation, so choose stable identity labels. Do not place a release version in the selector; versions change during every rollout.
The readiness probe controls whether a Pod is eligible to receive Service traffic. A running process is not necessarily ready: it may still be loading configuration, warming caches, or waiting for a dependency. The liveness probe has a different purpose and can restart a stuck container. Pointing both probes at a fragile dependency can turn an external outage into a restart loop.
Resource requests help the scheduler place Pods based on expected consumption. A memory limit can contain a leak, but a CPU limit can introduce throttling, so it should come from measurement rather than a copied default.
The rolling update permits one extra Pod and requires all desired replicas to remain available. That favors availability but needs temporary cluster capacity for the surge. For a non-critical environment, allowing one unavailable replica may use capacity more efficiently.
Validate and apply
Ask the API server to validate the object without persisting it:
kubectl apply \
--filename deployment.yaml \
--namespace applications \
--server-side \
--dry-run=server
Server-side dry run exercises API validation and admission policies. It can still differ from the final write if cluster state changes between commands, but it catches more than local parsing.
Apply the reviewed file and wait for the controller:
kubectl apply \
--filename deployment.yaml \
--namespace applications
kubectl rollout status \
deployment/release-api \
--namespace applications \
--timeout=5m
Do not treat kubectl apply returning zero as rollout success. It only confirms that the API accepted the desired state. kubectl rollout status waits for the Deployment to reach its completion criteria and returns a failure when the timeout is exceeded.
Observe the ownership chain
Inspect the Deployment first:
kubectl get deployment release-api \
--namespace applications \
--output wide
kubectl describe deployment release-api \
--namespace applications
Then list the controlled ReplicaSets and Pods using the same stable label:
kubectl get replicasets,pods \
--namespace applications \
--selector app.kubernetes.io/name=release-api \
--show-labels
The READY, UP-TO-DATE, and AVAILABLE Deployment columns answer different questions. A desired replica may exist without being ready, and an old ReplicaSet may remain during a rollout. Events near the bottom of kubectl describe often reveal image pull failures, failed scheduling, probe failures, or admission problems.
Perform a controlled rollout
Update the image in source control, review the manifest diff, and apply it. For an interactive demonstration, kubectl set image changes the Pod template directly:
kubectl set image deployment/release-api \
api=ghcr.io/example/release-api:1.1.0 \
--namespace applications
kubectl rollout status deployment/release-api \
--namespace applications \
--timeout=5m
Changing the Pod template creates a new ReplicaSet. Changing only the replica count does not. In a GitOps or infrastructure-as-code workflow, direct mutations such as set image can create drift, so commit the desired version and let the delivery controller perform the update.
Watch the rollout from another terminal when diagnosing timing:
kubectl get pods \
--namespace applications \
--selector app.kubernetes.io/name=release-api \
--watch
A useful rollout gate checks more than Pod readiness. Exercise a real application endpoint, inspect error rate and latency, and verify that the new image identity is visible in logs or metrics.
Roll back a bad revision
View recorded revisions:
kubectl rollout history deployment/release-api \
--namespace applications
Undo the latest rollout:
kubectl rollout undo deployment/release-api \
--namespace applications
kubectl rollout status deployment/release-api \
--namespace applications \
--timeout=5m
Rollback restores an earlier Pod template. It does not reverse a database migration, external configuration change, or irreversible side effect. Application releases need backward-compatible data changes and an operational rollback plan beyond the Deployment object.
Diagnose a stalled rollout
Work from the controller down instead of deleting Pods at random:
- Run
kubectl describe deploymentand inspect conditions and events. - Identify the new ReplicaSet and compare desired, current, and ready replicas.
- Describe a failing Pod to inspect scheduling and image events.
- Read current and previous container logs with
kubectl logsandkubectl logs --previous. - Confirm readiness behavior from inside the cluster if the probe path or network is in doubt.
Deleting a failing Pod only asks the ReplicaSet to recreate the same specification. Fix the image, configuration, resource constraint, probe, or policy that caused the failure.
Production checklist
- Use an immutable image digest or a version tag with controlled promotion.
- Keep selector labels stable and separate from release labels.
- Define resource requests from measured workload behavior.
- Use a readiness probe that represents the ability to serve traffic.
- Choose
maxSurgeandmaxUnavailableagainst real capacity and availability requirements. - Wait for rollout status in the deployment pipeline.
- Add application-level health and telemetry gates.
- Retain enough revision history for practical rollback.
- Keep the manifest in source control and avoid untracked direct mutations.
- Plan database and configuration compatibility independently of Kubernetes rollback.