Skip to main content
Kubernetes 101 — Pods, Deployments, and Services Explained

Kubernetes 101 — Pods, Deployments, and Services Explained

KU
• 5 min read•Updated •57 views

Run your first three Kubernetes objects — Pod, Deployment, Service — on a local cluster, then understand why each one exists and how they fit together.

By the end of this post you'll have a Kubernetes cluster running on your laptop, three core objects deployed to it (Pod, Deployment, Service), and a working mental model for what each one does. About 30 minutes start to finish.

You'll need Docker installed, plus kubectl and a local cluster tool (we'll use kind). Skip ahead to install steps if you don't have them yet.

The mental model

Kubernetes is a system for running container workloads across a fleet of machines. Three core objects show up first when you start using it:

  • A Pod is one or more containers running together as a unit. The smallest deployable thing in Kubernetes. Usually one container per Pod.
  • A Deployment manages a set of identical Pods. It says "I want N copies of this Pod running at all times." If a Pod dies, the Deployment makes another. If you change the image, it does a rolling update.
  • A Service is a stable network endpoint for a set of Pods. Pods come and go (different IPs each time); a Service has one IP that load-balances to whichever Pods are alive.

Almost everything else in Kubernetes (Ingresses, ConfigMaps, StatefulSets, autoscalers, RBAC) is an optimization on top of these three. Master these and you can read most production manifests.

Step 1: Install kubectl and kind

kubectl is the command-line tool that talks to the cluster. kind runs Kubernetes inside Docker so you can have a real cluster on your laptop in under a minute.

On Mac:

bash.bash
brew install kubectl kind

On Linux, follow the official docs for both. On Windows, use WSL2 or Docker Desktop's bundled Kubernetes.

Verify:

bash.bash
kubectl version --client
kind version

You should see version output for both.

Step 2: Start a local cluster

bash.bash
kind create cluster --name k8s-101

kind boots a cluster inside Docker. Takes about 30 seconds. When it finishes:

bash.bash
kubectl get nodes

You should see one node listed with status Ready. That's the whole cluster — one fake "machine" that's really just a Docker container.

Step 3: Run your first Pod

Save this as pod.yaml:

yaml.yaml
apiVersion: v1
kind: Pod
metadata:
  name: hello
spec:
  containers:
    - name: app
      image: nginx:1.27-alpine
      ports:
        - containerPort: 80

Apply it:

bash.bash
kubectl apply -f pod.yaml
kubectl get pods

You should see hello 1/1 Running 0 30s. The Pod is now running an nginx container.

Test it from inside the cluster:

bash.bash
kubectl exec hello -- curl -s http://localhost:80 | head -5

You should see nginx's default welcome page HTML.

So that's a Pod. One container, running. But Pods don't restart themselves if they die, and you can't easily run multiple copies. That's what Deployments are for.

Step 4: Replace the Pod with a Deployment

bash.bash
kubectl delete pod hello

Save this as deployment.yaml:

yaml.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello
spec:
  replicas: 3
  selector:
    matchLabels:
      app: hello
  template:
    metadata:
      labels:
        app: hello
    spec:
      containers:
        - name: app
          image: nginx:1.27-alpine
          ports:
            - containerPort: 80

Apply it:

bash.bash
kubectl apply -f deployment.yaml
kubectl get pods -l app=hello

You should see three Pods, each with 1/1 Running. The Deployment manages them.

Try killing one:

bash.bash
kubectl delete pod -l app=hello | head -1
kubectl get pods -l app=hello

You'll see the count drop briefly, then the Deployment spins up a replacement. That's the self-healing behavior — you say "I want 3", Kubernetes makes sure there's always 3.

Step 5: Add a Service

The three Pods each have their own IP, but those IPs are ephemeral — they change when Pods restart. A Service gives you one stable endpoint.

Save this as service.yaml:

yaml.yaml
apiVersion: v1
kind: Service
metadata:
  name: hello
spec:
  selector:
    app: hello
  ports:
    - port: 80
      targetPort: 80

Apply it:

bash.bash
kubectl apply -f service.yaml
kubectl get service hello

You'll see a ClusterIP like 10.96.123.45. That's the stable internal address. Anything in the cluster can hit that IP and reach one of your nginx Pods (load-balanced).

Test it:

bash.bash
kubectl run curl-test --image=curlimages/curl:latest -i --rm --restart=Never -- \
  curl -s http://hello.default.svc.cluster.local

You should see nginx HTML returned. The Service did the DNS resolution, picked one of your three Pods, and forwarded the request.

Step 6: Clean up

bash.bash
kind delete cluster --name k8s-101

That deletes the whole cluster. No leftover state on your machine.

Common mistakes

Forgetting the label selector. A Service uses selector to decide which Pods receive traffic. If your Service's selector doesn't match the Pod labels, traffic goes nowhere — silently. Always check kubectl get endpoints <service> to confirm Pods are linked.

Editing a Pod created by a Deployment. If you change a Deployment-managed Pod directly, the Deployment will overwrite your change on the next reconcile. Edit the Deployment, not the Pod.

Confusing Pod and Container. A Pod can have multiple containers (sidecars), but most Pods have one. The Pod is the scheduling unit; the container is what actually runs.

Using kubectl apply without seeing the diff. Run kubectl diff -f <file> before applying — it shows exactly what will change. Saves you from accidental destructive updates.

You've used the three core objects. The next levels:

Pods, Deployments, Services. Three objects, one mental model. Most production manifests are these three plus a handful of namespaces, configmaps, and secrets. Once they click, the rest is incremental.

Explore topics:KubernetesDevOps
React
Share this post
KU

Kiril Urbonas

AI Engineer

560 articles
View all articles by Kiril Urbonas

You might have missed

Evergreen posts worth revisiting.