Your First Deployment¶
A runnable lab, start to finish. You need kubectl pointed at a running cluster — if you're not there yet, finish Installing kubectl and a Local Cluster first.
What You'll Learn¶
- How to create a Deployment imperatively, then expose it with a Service
- How to watch a rollout complete and confirm Pods are actually healthy
- How to read logs and basic status output well enough to know something worked
Why This Matters¶
Reading YAML is not the same skill as watching a real rollout happen and knowing what "done" looks like. This lab is deliberately imperative (kubectl create, not a manifest file) so the first thing you build a feel for is the sequence — Deployment creates ReplicaSet creates Pods, Service gets endpoints, rollout reports done — before you start hand-writing YAML in Core Concepts.
Step 1 — Create a Deployment¶
$ kubectl create deployment hello-web --image=nginx:1.27 --replicas=3
deployment.apps/hello-web created
This creates a Deployment object with three replicas, running the pinned nginx:1.27 image — never nginx:latest, since an unpinned tag means you can't be sure what actually ends up running, or reproduce a bug later.
Step 2 — Watch It Come Up¶
$ kubectl get pods -l app=hello-web
NAME READY STATUS RESTARTS AGE
hello-web-6b9f4c8d7f-2xk4p 1/1 Running 0 8s
hello-web-6b9f4c8d7f-9j2md 1/1 Running 0 8s
hello-web-6b9f4c8d7f-qz7wn 1/1 Running 0 8s
kubectl create deployment automatically applied the label app=hello-web to the Pods it created — that's what the Deployment's selector uses internally to find "its" Pods.
Step 3 — Expose It with a Service¶
$ kubectl expose deployment hello-web --port=80 --target-port=80 --type=ClusterIP
service/hello-web exposed
$ kubectl get svc hello-web
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-web ClusterIP 10.96.142.201 <none> 80/TCP 5s
The Service picked up the same app=hello-web selector automatically, so it already routes to all three Pods. ClusterIP is reachable from inside the cluster only — see Services for the other types and when to use them.
Step 4 — Confirm It's Actually Reachable¶
$ kubectl run -it --rm debug --image=busybox:1.36 --restart=Never -- wget -qO- http://hello-web
<!DOCTYPE html>
<html>
<head><title>Welcome to nginx!</title></head>
...
kubectl run --rm spins up a throwaway Pod, runs one command, and deletes itself — a fast way to test in-cluster connectivity without leaving debris behind.
Step 5 — Trigger and Watch a Rollout¶
$ kubectl set image deployment/hello-web nginx=nginx:1.27.1
deployment.apps/hello-web image updated
$ kubectl rollout status deployment/hello-web
Waiting for deployment "hello-web" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "hello-web" rollout to finish: 2 out of 3 new replicas have been updated...
deployment "hello-web" successfully rolled out
kubectl set image bumps the container's image tag; the Deployment controller creates a new ReplicaSet and rolls Pods over from old to new, one at a time by default — the full mechanics of that rolling update live in ReplicaSets and Deployments.
Step 6 — Inspect Logs¶
$ kubectl get pods -l app=hello-web
NAME READY STATUS RESTARTS AGE
hello-web-7d8c9f6b5c-4mvpz 1/1 Running 0 40s
hello-web-7d8c9f6b5c-8wqrx 1/1 Running 0 38s
hello-web-7d8c9f6b5c-lk2nc 1/1 Running 0 35s
$ kubectl logs hello-web-7d8c9f6b5c-4mvpz
/docker-entrypoint.sh: Configuration complete; ready for start up
2026/08/24 10:15:02 [notice] 1#1: nginx/1.27.1
2026/08/24 10:15:02 [notice] 1#1: start worker processes
Note the Pod names changed entirely — they now carry a new ReplicaSet hash (7d8c9f6b5c instead of 6b9f4c8d7f), confirming the old ReplicaSet's Pods were replaced, not edited in place.
What Actually Happened¶
| Command | What it did |
|---|---|
kubectl create deployment |
Created a Deployment, which created a ReplicaSet, which created 3 Pods |
kubectl expose deployment |
Created a Service that selects Pods by the Deployment's auto-applied label |
kubectl set image |
Updated the Deployment's Pod template, triggering a new ReplicaSet and a rolling update |
kubectl rollout status |
Blocked until the new ReplicaSet's Pods were all Ready and the old ones scaled to zero |
kubectl logs <pod> |
Streamed stdout/stderr from a specific container inside a specific Pod |
Common Mistakes¶
- Using
nginx:latestin a first deployment out of habit — it's the one habit worth breaking on day one, since it also makeskubectl rollout undomeaningless (there's no distinct prior tag to roll back to). - Running
kubectl exposebefore checkingkubectl get podsshowsRunning— a Service with no ready backing Pods will exist but return nothing. - Not noticing the Pod name's hash suffix changes after a rollout — that's your fastest visual confirmation that a genuinely new ReplicaSet (not a live-edited old one) was created.
Interview Questions¶
- Walk through what objects get created when you run
kubectl create deployment. - How would you confirm a rollout actually finished, versus just kicked off?
- Why does a rolling update change the Pod name's hash suffix instead of reusing the existing Pods?
See Interview Prep for full answers.
Next¶
Continue to Core Concepts to build the vocabulary behind everything you just ran — Pods, Deployments, Services, and more, in YAML this time.