# Kubernetes Fundamentals: Pods, Deployments, Services

- URL: https://karandeepsingh.ca/posts/kubernetes-fundamentals-pods-deployments-services/
- Author: Karandeep Singh
- Published: 2026-06-18
- Series: Kubernetes and Containers
- Tags: kubernetes, containers, devops
- Summary: A hands-on introduction to Kubernetes fundamentals — pods, deployments, replicasets, and services — built step by step on a local kind cluster, with the mistakes and fixes that make the concepts stick.

Kubernetes has a reputation for being overwhelming, but the core of it rests on three objects: **pods**, **deployments**, and **services**. Learn these Kubernetes fundamentals and most of the rest of the system suddenly makes sense.

This guide is hands-on. You will spin up a real cluster on your laptop with `kind`, deploy an app the naive way, watch it fail to self-heal, and then fix it the Kubernetes way. By the end you will understand not just *what* pods, deployments, and services are, but *why* each one exists.

![Kubernetes fundamentals — pods, deployments, and services on a local cluster](https://karandeepsingh.ca/images/posts/kubernetes-fundamentals-card.jpg)

## Set Up a Local Cluster with kind

`kind` runs a Kubernetes cluster inside Docker containers — perfect for learning the fundamentals without a cloud bill. You need Docker installed, then:

```bash
# macOS / Linux (Homebrew)
brew install kind kubectl

# or grab the binary directly
curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64
chmod +x ./kind && sudo mv ./kind /usr/local/bin/kind

kind create cluster --name learn
kubectl get nodes
```

If `kubectl get nodes` shows a node in `Ready` state, you have a working cluster. Every command in this article runs against it. If you want a refresher on the commands themselves, keep the [kubectl cheat sheet](https://karandeepsingh.ca/posts/kubectl-cheat-sheet/) open in another tab.

## Pods: The Smallest Deployable Unit

A **pod** wraps one or more containers that share a network and storage. You rarely create pods directly, but doing it once shows why you shouldn't. Create a bare pod:

```bash
kubectl run web --image=nginx
kubectl get pods -o wide
```

Now simulate a failure by deleting it:

```bash
kubectl delete pod web
kubectl get pods
```

The pod is gone. Nothing brings it back. That is the key lesson of this section: **a bare pod has no self-healing.** If its node crashes or someone deletes it, your app stays down. Pods are the unit of execution, not the unit you manage.

**info:**
A pod gets a fresh IP address every time it is created. That detail — ephemeral pod IPs — is the entire reason Services exist, which we will get to shortly.

## Deployments: Self-Healing and Scaling

A **Deployment** is the object you actually use. It manages a ReplicaSet, which guarantees a desired number of identical pods are always running. Write your first manifest:

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

Apply it and watch three pods appear:

```bash
kubectl apply -f web-deploy.yaml
kubectl get pods -l app=web
```

Now delete one pod and watch the difference:

```bash
kubectl delete pod $(kubectl get pods -l app=web -o name | head -1)
kubectl get pods -l app=web
```

A replacement pod appears within seconds. The Deployment noticed reality (2 pods) drifted from desired state (3 pods) and reconciled it. That reconciliation loop is the heart of Kubernetes.

```mermaid
graph TD
  A[Deployment: replicas=3] --> B[ReplicaSet]
  B --> C[Pod 1]
  B --> D[Pod 2]
  B --> E[Pod 3]
  E -. deleted .-> F[ReplicaSet creates a replacement]
  F --> G[Pod 3 - new]
```

Scaling is a one-liner — no manifest edit required for a quick change:

```bash
kubectl scale deploy/web --replicas=5
kubectl rollout status deploy/web
```

## Services: A Stable Address for Ephemeral Pods

Your three pods each have their own IP, and those IPs change constantly. A client cannot chase them. A **Service** solves this by giving the set of pods one stable virtual IP and DNS name, then load-balancing requests across whichever pods currently match its label selector.

```yaml
# web-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  selector:
    app: web        # routes to every pod labeled app=web
  ports:
  - port: 80
    targetPort: 80
```

```bash
kubectl apply -f web-svc.yaml
kubectl get svc web
kubectl get endpoints web      # the pod IPs the Service is routing to
```

The `endpoints` output is the magic: it lists the live pod IPs behind the stable Service IP, updated automatically as pods come and go. Test it from your laptop with a port-forward:

```bash
kubectl port-forward svc/web 8080:80
# now visit http://localhost:8080
```

**warning:**
The most common Service mistake is a selector that does not match your pod labels. If `kubectl get endpoints web` is empty, the Service is routing to nothing — line up the Service `selector` with the Deployment's pod `labels` exactly.

## How the Three Objects Fit Together

Put the fundamentals in one sentence: a **Deployment** keeps the right number of **pods** running, and a **Service** gives those pods a stable front door. Everything else in Kubernetes — ConfigMaps, Ingress, autoscaling, probes — builds on this spine.

**What about ReplicaSets — do I manage those?**
No. A Deployment creates and manages ReplicaSets for you. When you change the pod template (say, a new image), the Deployment creates a *new* ReplicaSet and scales it up while scaling the old one down — that is how rolling updates work. You almost never create a ReplicaSet by hand.

## Common Beginner Pitfalls

- **Creating bare pods.** Always use a Deployment so your workload self-heals. Bare pods are for one-off debugging only.
- **Mismatched labels and selectors.** The Deployment's `template.metadata.labels` must match both its own `selector.matchLabels` and the Service's `selector`.
- **Expecting `kubectl edit` to stick.** Editing a live object managed by `apply` gets reverted on the next deploy. Change the manifest in Git instead.
- **A pod stuck in CrashLoopBackOff.** That means the container starts and dies repeatedly. It deserves its own playbook — see [how to fix Kubernetes CrashLoopBackOff](https://karandeepsingh.ca/posts/fix-kubernetes-crashloopbackoff/).

## Where to Go Next

Once the local fundamentals click, the natural next step is a real cloud cluster. Moving from `kind` to a managed control plane is straightforward with [EKS setup on AWS using eksctl](https://karandeepsingh.ca/posts/kubernetes-eks-setup-eksctl/). From there, autoscaling, Ingress, and Helm are incremental additions on top of the same pod-deployment-service spine.

**Question:** What was the concept that finally made Kubernetes click for you — the reconciliation loop, labels, or something else?

## References and Further Reading

- Kubernetes Authors. [Kubernetes Concepts](https://kubernetes.io/docs/concepts/). Kubernetes Documentation.
- Kubernetes Authors. [Deployments](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/). Kubernetes Documentation.
- Kubernetes Authors. [Service](https://kubernetes.io/docs/concepts/services-networking/service/). Kubernetes Documentation.
- The kind Authors. [kind — Kubernetes IN Docker](https://kind.sigs.k8s.io/). kind Documentation.
- Burns, B., Beda, J., Hightower, K., & Evenson, L. (2022). [Kubernetes: Up and Running, 3rd Edition](https://www.oreilly.com/library/view/kubernetes-up-and/9781098110192/). O'Reilly Media.

## Quiz

1. What happens to a bare pod when the node it runs on dies?
   a) Kubernetes reschedules it onto another node
   b) It is gone — nothing recreates it
   c) It restarts on the same node automatically
   d) It pauses until the node returns
   Answer: b. A bare pod has no controller, so nothing recreates it. A Deployment (via its ReplicaSet) is what reschedules replacement pods when a node fails.

2. Which Kubernetes object keeps a desired number of pod replicas running?
   a) Service
   b) ConfigMap
   c) Deployment
   d) Namespace
   Answer: c. A Deployment manages a ReplicaSet, which continuously reconciles the actual pod count to match the desired replica count.

3. Why do you put a Service in front of your pods?
   a) To store environment variables
   b) Pods get ephemeral IPs; a Service gives a stable address and load-balances
   c) To build container images
   d) To grant cluster admin access
   Answer: b. Pod IPs change every time a pod is recreated. A Service provides one stable virtual IP and DNS name and load-balances across the matching pods.

4. Which command scales a Deployment named web to 4 replicas?
   a) kubectl run web --replicas=4
   b) kubectl scale deploy/web --replicas=4
   c) kubectl get deploy/web --replicas=4
   d) kubectl expose deploy/web --replicas=4
   Answer: b. `kubectl scale deploy/web --replicas=4` updates the desired replica count; the ReplicaSet then creates or removes pods to match.


