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

← Part 2 · Blog index · Part 4 →

Part 2 Part 4