Your First Workloads: Pod, Deployment, and Service
A lone Pod is a teaching tool; a Deployment is how you run stateless apps in production; a Service gives stable networking to ephemeral Pods. This post walks through all three on your local cluster—with commands you can run in ten minutes.
In short
Create a Deployment with multiple replicas, confirm Pods with labels, expose port 80 with a ClusterIP Service, and reach the app via port-forward. Delete with the same YAML file you applied.
Mental model
- Pod — one or more containers that share network and storage; smallest schedulable unit.
- Deployment — declares desired replicas and rollout strategy; owns ReplicaSets, which own Pods.
- Service — stable DNS name and virtual IP targeting Pods by label.
For architecture context see Kubernetes architecture in simple terms. For how traffic flows from Ingress through Services to Pods, see Kubernetes networking in depth.
Step 1 — Deployment (three replicas)
Save as web.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
labels:
app.kubernetes.io/name: web
spec:
replicas: 3
selector:
matchLabels:
app.kubernetes.io/name: web
template:
metadata:
labels:
app.kubernetes.io/name: web
spec:
containers:
- name: nginx
image: nginx:1.25-alpine
ports:
- containerPort: 80
kubectl apply -f web.yaml
kubectl get deployment web
kubectl get pods -l app.kubernetes.io/name=web
You should see three Pods, each with a unique name suffix. Kill one Pod with kubectl delete pod <name> and watch the Deployment replace it—reconciliation in action.
Step 2 — Service (ClusterIP)
Append to the same file after --- or save as web-svc.yaml:
apiVersion: v1
kind: Service
metadata:
name: web
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: web
ports:
- port: 80
targetPort: 80
kubectl apply -f web-svc.yaml # or combined file
kubectl get svc web
kubectl get endpoints web
endpoints should list Pod IPs. If empty, your Service selector does not match Pod labels—return to Part 2’s selector section.
Step 3 — Reach the app locally
On a laptop cluster there is usually no cloud load balancer. Use port-forward:
kubectl port-forward svc/web 8080:80
# In another terminal:
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8080/
Expect HTTP 200. For minikube you can also try minikube service web when using certain addons—port-forward works everywhere.
Optional — one-off Pod (know the difference)
apiVersion: v1
kind: Pod
metadata:
name: debug-once
spec:
containers:
- name: shell
image: busybox:1.36
command: ["sh", "-c", "echo hello && sleep 30"]
Pods created alone are not self-healing. Use them for debugging or Jobs—not for serving customer traffic.
Rollout exercise
kubectl set image deployment/web nginx=nginx:1.27-alpine
kubectl rollout status deployment/web
kubectl rollout history deployment/web
Prefer changing image tags in YAML and kubectl apply when practicing GitOps habits. Imperative set image is fine for labs.
Clean up
kubectl delete -f web.yaml
# include Service manifest if separate