| Tested on | Rocky Linux 10.2 (Red Quartz) workstation |
|---|---|
| Package | kubectl 1.36.3 |
| Applies to | Any host with kubectl configured; any Kubernetes cluster |
| Cert prep | CKA · CKAD |
| Lab environment | Multi-node kubeadm cluster with containerd — install Kubernetes with kubeadm |
| Privilege | Normal user (no sudo required on the workstation) |
| Scope | Deployment YAML, imperative create, scaling, image updates, RollingUpdate strategy, maxSurge and maxUnavailable, rollout status, pause and resume, revision history, rollback, Deployment conditions, and common rollout problems. Does not cover HPA, blue-green or canary procedures, StatefulSet or DaemonSet rollouts, GitOps, Helm, readiness probe depth, PodDisruptionBudget, or progressive delivery controllers. |
This walkthrough uses the deploy-lab namespace and one nginx Deployment named web. You will create and scale it, roll out nginx:1.27.0 from nginx:1.25.4, pause the Deployment before a template change, batch updates, and resume one rollout, then inspect revision history and roll back a failed release.
All rollout exercises use kubectl set image and related imperative commands so the revision sequence is reproducible. Do not also edit or reapply web-deploy.yaml during the rollout and rollback exercises.
Imperative image, environment, pause/resume, and rollback commands change the live Deployment but do not update web-deploy.yaml. Reapplying the unchanged file later can restore its old image, replica count, and Pod-template configuration.
What Is a Kubernetes Deployment?
A Deployment manages interchangeable application Pods for long-running stateless workloads. It creates and manages ReplicaSets, maintains the desired replica count, and supports scaling, rolling updates, and rollbacks.
The ownership chain:
Deployment → ReplicaSet → Pods
For when to pick a bare Pod versus a Deployment, see Pod versus Deployment. For how ReplicaSets maintain Pod counts before rollout features, see Kubernetes ReplicaSet.
Create and Understand a Deployment
Imperative creation
For a quick one-replica Deployment, imperative create is enough:
kubectl create namespace deploy-labSample output:
namespace/deploy-lab createdkubectl create deployment quick --image=nginx:1.25.4 -n deploy-labSample output:
deployment.apps/quick createdkubectl wait --for=condition=Available deployment/quick -n deploy-lab --timeout=120sSample output:
deployment.apps/quick condition metA Deployment becomes Available when enough replicas are Ready and have remained Ready for minReadySeconds. The default minReadySeconds is zero.
kubectl get deployments -n deploy-labSample output:
NAME READY UP-TO-DATE AVAILABLE AGE
quick 1/1 1 1 3sDelete the quick Deployment before the main YAML lab:
kubectl delete deployment quick -n deploy-lab --wait=trueSample output:
deployment.apps "quick" deletedDeployment YAML
Save this manifest as web-deploy.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: deploy-lab
labels:
app: web
spec:
replicas: 3
revisionHistoryLimit: 5
progressDeadlineSeconds: 60
selector:
matchLabels:
app: web
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.25.4spec.replicassets how many Pods the Deployment should runrevisionHistoryLimit: 5retains old ReplicaSets for rollbackprogressDeadlineSeconds: 60lets the lab surface a failed rollout quicklyspec.selector.matchLabelsmust matchspec.template.metadata.labels- The container image uses an explicit version tag so updates are visible later
kubectl apply -f web-deploy.yamlSample output:
deployment.apps/web createdSelector, Pod template, and ReplicaSet ownership
- The Deployment
spec.selectoridentifies Pods it manages - Labels on
spec.template.metadata.labelsmust match that selector; Deployment selectors are immutable after creation - Kubernetes also adds
pod-template-hashon each ReplicaSet and its Pods; that hash is not part of your Deployment selector - Changes under
spec.templatecreate a new rollout and ReplicaSet when the Deployment is active; while paused, Kubernetes waits until the Deployment is resumed - Editing Deployment metadata outside the Pod template, such as
metadata.labelson the Deployment object, does not trigger a rollout
Verify Pods and ReplicaSets
Wait until the Deployment reports Available, then confirm the replica counts:
kubectl wait --for=condition=Available deployment/web -n deploy-lab --timeout=120skubectl get deployments -n deploy-labSample output:
NAME READY UP-TO-DATE AVAILABLE AGE
web 3/3 3 3 5sColumn meanings on kubectl get deployments:
READY— ready replicas versus desired (3/3)UP-TO-DATE— Pods running the latest Pod templateAVAILABLE— replicas that satisfy the Deployment's availability requirement
kubectl get replicasets -n deploy-labSample output:
NAME DESIRED CURRENT READY AGE
web-86b6cb7b94 3 3 3 5skubectl get pods -n deploy-lab -l app=webSample output:
NAME READY STATUS RESTARTS AGE
web-86b6cb7b94-dgqxl 1/1 Running 0 5s
web-86b6cb7b94-f4hx7 1/1 Running 0 4s
web-86b6cb7b94-nmp5s 1/1 Running 0 4skubectl describe deployment web -n deploy-labSample output (trimmed):
Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable
RollingUpdateStrategy: 0 max unavailable, 1 max surge
Image: nginx:1.25.4
NewReplicaSet: web-86b6cb7b94 (3/3 replicas created)This initial Pod template is revision 1 with nginx:1.25.4.
Scale a Deployment
Imperative scaling
Increase the Deployment to four replicas. Scaling edits spec.replicas on the live object and does not change the Pod template, so rollout revision history stays at revision 1.
kubectl scale deployment web -n deploy-lab --replicas=4Sample output:
deployment.apps/web scaledWait until four Pods report Ready before you continue.
kubectl wait --for=jsonpath='{.status.readyReplicas}'=4 deployment/web -n deploy-lab --timeout=120sConfirm the Deployment status columns reflect the new count:
kubectl get deployment web -n deploy-labSample output:
NAME READY UP-TO-DATE AVAILABLE AGE
web 4/4 4 4 7sFour Pods now match the desired count. Scaling alone does not create a new rollout revision.
Reapply declarative replica count
kubectl scale changed the live Deployment to four replicas but did not update web-deploy.yaml. Reapply the unchanged manifest to restore its declared replica count of three.
kubectl apply -f web-deploy.yamlSample output:
deployment.apps/web configuredWait until the scale-down completes and three Pods are Ready again.
kubectl wait --for=jsonpath='{.status.readyReplicas}'=3 deployment/web -n deploy-lab --timeout=120sConfirm the Deployment returned to three replicas:
kubectl get deployment web -n deploy-labSample output:
NAME READY UP-TO-DATE AVAILABLE AGE
web 3/3 3 3 8sIf you scale imperatively and later reapply a manifest with a different replicas value, the manifest becomes the desired state again. Horizontal Pod Autoscaler is outside this article.
Roll Out a New Application Version
Update the image
Roll the Deployment to nginx:1.27.0 with an imperative image change:
kubectl set image deployment/web nginx=nginx:1.27.0 -n deploy-labSample output:
deployment.apps/web image updatedA Pod-template change starts a new rollout. This becomes revision 2.
Watch old and new ReplicaSets
Immediately watch ReplicaSets and Pods:
kubectl get rs,pod -n deploy-lab -l app=web --watchSample output (trimmed) during the rollout:
NAME DESIRED CURRENT READY AGE
replicaset.apps/web-5b6bd7f99b 2 2 1 4s
replicaset.apps/web-86b6cb7b94 2 2 2 12s
NAME READY STATUS RESTARTS AGE
pod/web-5b6bd7f99b-lk96n 0/1 ContainerCreating 0 2s
pod/web-5b6bd7f99b-q6bcm 1/1 Running 0 4s
pod/web-86b6cb7b94-dgqxl 1/1 Running 0 12s
pod/web-86b6cb7b94-f4hx7 1/1 Terminating 0 11s
pod/web-86b6cb7b94-nmp5s 1/1 Running 0 11sObserve the new ReplicaSet scaling up and the old one scaling down. Press Ctrl+C after the new ReplicaSet reaches three Ready Pods.
kubectl rollout status follows the latest rollout until it completes, so running it first removes the intermediate state this section demonstrates.
Confirm the deployed template
Confirm the rollout completed successfully:
kubectl rollout status deployment/web -n deploy-lab --timeout=120sSample output (trimmed):
Waiting for deployment "web" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "web" rollout to finish: 1 old replicas are pending termination...
deployment "web" successfully rolled outList ReplicaSets to confirm the old set scaled to zero:
kubectl get replicasets -n deploy-labSample output:
NAME DESIRED CURRENT READY AGE
web-5b6bd7f99b 3 3 3 7s
web-86b6cb7b94 0 0 0 15sRead the image from the live Deployment template:
kubectl describe deployment web -n deploy-lab | grep 'Image:'Sample output:
Image: nginx:1.27.0Or read the template image directly:
kubectl get deployment web -n deploy-lab -o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'Sample output:
nginx:1.27.0Configure Deployment Update Strategy
RollingUpdate
RollingUpdate is the default Deployment update strategy. It replaces Pods incrementally instead of deleting all old Pods first.
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0maxSurgeandmaxUnavailableaccept absolute numbers or percentagesmaxUnavailablepercentages are rounded downmaxSurgepercentages are rounded up- Both fields cannot resolve to zero at the same time
- Defaults are
25%for each when not specified
maxSurge and maxUnavailable
| Field | Controls |
|---|---|
maxSurge |
Extra Pods allowed above the desired replica count |
maxUnavailable |
Desired Pods allowed to be unavailable |
Practical presets:
- Availability-focused:
maxSurge: 1,maxUnavailable: 0— keep capacity while surging one extra Pod - Capacity-constrained:
maxSurge: 0,maxUnavailable: 1— no surge; replace in place one Pod at a time
maxSurge controls how many extra Pods may run above the desired replica count during an update. With four desired replicas and maxSurge: 1, the cluster may briefly run five Pods while one new Pod starts before an old Pod terminates.
maxUnavailable controls how many desired Pods may be unavailable during the rollout. With maxSurge: 0 and maxUnavailable: 1, Kubernetes must first remove an old Pod before creating its replacement.
Example with four replicas and maxSurge: 0, maxUnavailable: 1:
| Stage | Old Pods | New Pods | Total non-terminating Pods | Available Pods |
|---|---|---|---|---|
| Start | 4 | 0 | 4 | 4 |
| Scale down one old Pod | 3 | 0 | 3 | 3 |
| Start one new Pod | 3 | 1 | 4 | 3 until the new Pod is Ready |
| New Pod becomes Ready | 3 | 1 | 4 | 4 |
| Repeat | Decreases | Increases | 3 or 4 | At least 3 |
| Complete | 0 | 4 | 4 | 4 |
No non-terminating Pod count exceeds the desired four replicas. During each replacement, availability may temporarily fall to three until the new Pod becomes Ready.
Recreate strategy
Kubernetes also supports Recreate:
- Terminates all old Pods before starting new ones, causing brief downtime
- Fits maintenance windows or workloads that cannot run two versions side by side
For broader workload and strategy selection, see choose a Kubernetes workload resource. Blue-green and canary patterns use separate Deployments and Service selectors — see Kubernetes blue-green and canary deployments.
Terminating Pods during rollout
- Terminating Pods are not counted the same way as active rollout replicas
- Their grace periods can make the total number of Pod objects—and actual resource use—temporarily exceed
replicas + maxSurge - Kubernetes 1.36 can expose these through
.status.terminatingReplicaswhen the beta feature is enabled, which it is by default
These are part of current Deployment rollout accounting.
Pause and Resume a Deployment
Batch Pod-template changes
Revision 3 in this lab starts from a paused template change rather than an image revert.
kubectl rollout pause deployment/web -n deploy-labSample output:
deployment.apps/web pausedMake a different Pod-template change instead of reverting the image:
kubectl set env deployment/web RELEASE_STAGE=paused-demo -n deploy-labSample output:
deployment.apps/web env updatedWhile paused, Pod-template edits are stored on spec.template, but they do not create a new ReplicaSet or rollout revision. A paused Deployment cannot be rolled back until it is resumed.
Verify paused state
kubectl rollout history deployment/web -n deploy-labSample output:
deployment.apps/web
REVISION CHANGE-CAUSE
1 <none>
2 <none>Rollout history is unchanged while the Deployment stays paused.
kubectl get deployment web -n deploy-labSample output:
NAME READY UP-TO-DATE AVAILABLE AGE
web 3/3 0 3 16sAVAILABLE stays at three because old Pods still run, but UP-TO-DATE is 0 because the new template has not rolled out.
kubectl get deployment web -n deploy-lab -o jsonpath='Paused: {.spec.paused}{"\n"}'Sample output:
Paused: trueResume one rollout
kubectl rollout resume deployment/web -n deploy-labSample output:
deployment.apps/web resumedWait for the resumed rollout to finish:
kubectl rollout status deployment/web -n deploy-lab --timeout=120sThe resumed template change becomes revision 3. Kubernetes does not start a rollout for Pod-template edits while a Deployment remains paused.
kubectl rollout history deployment/web -n deploy-lab --revision=3Sample output (trimmed):
deployment.apps/web with revision #3
Pod Template:
Labels: app=web
pod-template-hash=6786685cc6
Containers:
nginx:
Image: nginx:1.27.0
Environment:
RELEASE_STAGE: paused-demoInspect Revisions and Roll Back
View revision history
kubectl rollout history deployment/web -n deploy-labSample output:
deployment.apps/web
REVISION CHANGE-CAUSE
1 <none>
2 <none>
3 <none>Each Pod-template change creates a new revision. Scaling alone does not add a revision.
Create a failed release
Create a deliberately failed rollout:
kubectl set image deployment/web nginx=nginx:not-a-real-tag -n deploy-labSample output:
deployment.apps/web image updatedkubectl rollout status deployment/web -n deploy-lab --timeout=75sSample output:
error: deployment "web" exceeded its progress deadlineInspect the new ReplicaSet and failing Pod before rollback:
kubectl get rs,pod -n deploy-lab -l app=webSample output:
NAME DESIRED CURRENT READY AGE
replicaset.apps/web-54d5c4cc5c 1 1 0 61s
replicaset.apps/web-6786685cc6 3 3 3 68s
NAME READY STATUS RESTARTS AGE
pod/web-54d5c4cc5c-ff6r9 0/1 ErrImagePull 0 61s
pod/web-6786685cc6-7g9jj 1/1 Running 0 68s
pod/web-6786685cc6-npktg 1/1 Running 0 64s
pod/web-6786685cc6-tkfvz 1/1 Running 0 66sWith maxUnavailable: 0, the three previous Pods remain available while the bad ReplicaSet has one surge Pod in ErrImagePull or ImagePullBackOff. This becomes revision 4.
Roll back to the previous revision
kubectl rollout undo deployment/web -n deploy-labSample output:
deployment.apps/web rolled backThis restores revision 3's template and creates a new current revision, normally revision 5.
Wait for the rollback rollout to finish:
kubectl rollout status deployment/web -n deploy-lab --timeout=120sList the revision numbers again after the rollback:
kubectl rollout history deployment/web -n deploy-labSample output:
deployment.apps/web
REVISION CHANGE-CAUSE
1 <none>
2 <none>
4 <none>
5 <none>A rollback restores an older Pod template as a new current revision; revision numbers and the entries shown in history therefore change after rollback. Always rerun rollout history before using --to-revision.
kubectl describe deployment web -n deploy-lab | grep 'Image:'Sample output:
Image: nginx:1.27.0The Pod template returned to revision 3's image and environment variables.
Roll back to a specific revision
Revision 2 and revision 3 both use nginx:1.27.0; their difference is the RELEASE_STAGE environment variable. Rolling back to revision 1 makes the result visible because it restores the original nginx:1.25.4 image.
Inspect history again and select a revision that is still listed:
kubectl rollout history deployment/web -n deploy-labRoll back to revision 1:
kubectl rollout undo deployment/web -n deploy-lab --to-revision=1Sample output:
deployment.apps/web rolled backThis restores the original revision 1 template with nginx:1.25.4.
Wait for that rollout to complete:
kubectl rollout status deployment/web -n deploy-lab --timeout=120sConfirm the container image on the live template:
kubectl get deployment web -n deploy-lab -o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'Sample output:
nginx:1.25.4Rollback restores the selected Pod template. It does not revert unrelated manual scaling performed outside the template.
revisionHistoryLimit
revisionHistoryLimit: 5retains up to five old ReplicaSets after completed rollouts- It does not count the active ReplicaSet
- Cleanup happens only after the Deployment reaches a complete state, so a stuck Deployment may temporarily retain more old ReplicaSets than the configured limit
- Setting the value to
0removes rollback history
Deployment history is stored in its ReplicaSets; once one is cleaned up, that revision can no longer be restored.
Monitor and Troubleshoot Rollouts
Deployment conditions
kubectl describe deployment reports conditions such as:
Available— minimum replicas satisfy the Deployment's availability requirementProgressing— rollout is active or completed successfullyReplicaFailure— replica creation failed
Sample output (trimmed):
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailableNo Service is created in this article, so Kubernetes is not verifying that the Pods are actually receiving traffic.
Progress deadline
progressDeadlineSecondscontrols when Kubernetes reports that the rollout has stopped making progress- It does not terminate the rollout or automatically roll it back; the Deployment controller continues retrying
- Time spent with the Deployment paused is not counted toward the progress deadline
When the deadline is exceeded, the condition becomes:
type: Progressing
status: "False"
reason: ProgressDeadlineExceededCommon rollout problems
| Symptom | Likely cause | Fix |
|---|---|---|
| Rollout waiting for new Pods | Pods not Ready |
Inspect Pod status and Events; see Kubernetes Pods and Pod Lifecycle |
ProgressDeadlineExceeded |
New Pods never become healthy | Check the new ReplicaSet Pods; roll back if the release is bad |
| Deployment not creating Pods | Zero replicas, selector mismatch, quota, or admission error | Fix Deployment not creating Pods |
| Old ReplicaSet not scaling down | New Pods not becoming Ready |
Check readiness and rollout availability settings; see Kubernetes health probes |
| Image update did not roll out | Image changed outside spec.template.spec.containers |
Edit the Pod template container image, not unrelated fields |
| Reapply restored old image or replica count | Manifest is declarative desired state | Update the YAML before kubectl apply, or align imperative changes with the manifest |
| Image pull failures on new Pods | Wrong tag, registry auth, or network | Verify image name and registry access |
For Pod scheduling, image pull, and ContainerCreating problems, inspect individual Pods with kubectl describe pod and Events.
What's Next
- Kubernetes StatefulSet with Examples
- Kubernetes DaemonSet with Examples
- Kubernetes Jobs with Examples
References
Summary
You created a Deployment, verified ReplicaSet and Pod ownership, scaled replicas, and rolled out a new nginx image with RollingUpdate. maxSurge and maxUnavailable trade rollout speed against capacity during the change. kubectl rollout status, rollout history, and ReplicaSet lists show where a release stands.
Pause and resume let you batch Pod-template edits before they hit the cluster. The lab paused before an environment-variable change, resumed one rollout as revision 3, created a failed image release, rolled back to the previous healthy template, and restored revision 1 with --to-revision=1 to return to nginx:1.25.4. Watch UP-TO-DATE and AVAILABLE together during rollouts, especially when a Deployment is paused or new Pods fail readiness checks.
StatefulSet and DaemonSet rollouts follow different controller rules and are not covered here.

