Run your app on a real cluster — that heals and scales itself.
Module 8 · The notes app goes from one machine to a cluster. Free · Local with Minikube.
Beginner+ Orchestration ~60 minBy the end of this module you will be able to:
kubectlPrerequisites: Module 7 — Compose. You should understand images, containers, volumes, and env vars.
Compose is perfect for one machine. But what happens in the real world when…
With Compose, it stays down until a human notices. What if you need 10 copies of your app across 5 servers? What if one server dies? What about rolling out a new version with zero downtime? Compose has no answer. Kubernetes (K8s) does.
Kubernetes is an orchestrator: you declare the desired state ("I want 3 copies of this app, always running"), and K8s constantly works to make reality match — restarting crashed containers, rescheduling them when a server dies, spreading load across machines.
| Docker Compose | Kubernetes | |
|---|---|---|
| Scope | One machine | A cluster of many machines |
| If a container dies | Stays dead | Auto-restarted |
| Scaling | Manual, one host | Declare replicas, any host |
| Updates | Restart (downtime) | Rolling, zero-downtime |
| Best for | Local dev, small apps | Production, scale, HA |
K8s is famous for being complex — but the core is just a handful of objects. Today you'll learn the 5 that cover 90% of real use. Everything else builds on them.
| Object | What it is | Analogy |
|---|---|---|
| Cluster | The whole system: a control plane + worker machines. | The factory |
| Node | A single machine (VM or physical) that runs your containers. | A worker on the floor |
| Pod | The smallest unit K8s runs — wraps one (or more) containers. | One running container |
| Deployment | Manages a set of identical pods: keeps N running, handles updates & self-healing. | A manager who keeps N workers staffed |
| Service | A stable network address + load balancer in front of pods (which come and go). | A receptionist with one phone number |
You almost never create pods directly. You create a Deployment ("keep 3 of these running"), which creates Pods, and a Service gives those pods one stable address — because individual pods get replaced and their IPs change.
In Compose you say "run this." In K8s you write a YAML file describing the desired state and run kubectl apply. K8s figures out how to get there — and keeps it that way.
Minikube runs a real single-node Kubernetes cluster on your laptop (free). kubectl is the command-line tool you use to talk to any cluster.
Get Minikube from minikube.sigs.k8s.io and kubectl from kubernetes.io/docs/tasks/tools. Minikube needs Docker (from Module 6) as its driver.
Start your cluster and confirm it's alive:
You should see one node with status Ready — your personal Kubernetes cluster is running. 🎉
We'll deploy the notes app + Postgres from Module 7 onto Kubernetes. Work in your notes-app folder and create a k8s/ subfolder for the manifests.
Minikube has its own image store. Load the notes-app image you built in Module 6 into it:
Normally pods pull images from a registry (Docker Hub from Module 6). minikube image load is a local shortcut so you don't have to push first.
k8s/db-secret.yamlNever put passwords in plain manifests. A Secret holds sensitive values (here Kubernetes encodes them for you with stringData).
k8s/db.yamlA Deployment runs the database pod; a Service gives it the stable name db so the web app can find it (just like in Compose).
This Postgres pod has no persistent volume, so data resets if the pod restarts — fine for learning. Real stateful databases use a StatefulSet + PersistentVolumeClaim (or a managed cloud DB). We keep it simple to focus on the core objects.
k8s/web.yamlThis is the star: replicas: 2 means K8s keeps two copies running. The Service load-balances across them and exposes the app via NodePort.
db resolution worksThe web pods reach the database at host db — that's the Service name from Step 3. Kubernetes' built-in DNS turns service names into addresses, exactly like Compose did.
Point kubectl at the whole folder — it applies every manifest:
You should see two web pods and one db pod. The Deployment created them for you.
Minikube gives you a URL for the NodePort service:
Your browser opens the notes app — now served by a load-balanced pair of pods on your cluster. ✅
The same app from Module 6 now runs as 2 replicas behind a Service on a real Kubernetes cluster — defined entirely in declarative YAML.
This is the magic moment. Delete a running pod and watch Kubernetes replace it within seconds — because the Deployment's desired state says "keep 2 running."
Change the desired number of replicas with one command — no editing files needed:
Every Kubernetes object shares the same 4-part skeleton. Recognize it once and every manifest reads the same.
| Field | What it means |
|---|---|
apiVersion | Which API to use (e.g. apps/v1 for Deployments, v1 for Services/Secrets). |
kind | The object type: Deployment, Service, Secret, Pod… |
metadata | Name and labels that identify the object. |
spec | The desired state — what you want to exist. |
A Deployment stamps pods with a label (app: web). The Service's selector: app: web says "send traffic to any pod with that label." That loose coupling is how Services keep working as pods are created and destroyed.
The commands you'll use every day. Bookmark this.
| Command | What it does |
|---|---|
kubectl apply -f file.yaml | Create/update objects from a manifest (or a whole folder) |
kubectl get pods | List pods (add -w to watch live) |
kubectl get all | List pods, deployments, services together |
kubectl describe pod <name> | Detailed status + events (your #1 debug tool) |
kubectl logs <pod> | Show a pod's logs (-f to follow) |
kubectl exec -it <pod> -- bash | Open a shell inside a pod |
kubectl scale deploy web --replicas=N | Change how many copies run |
kubectl delete pod <name> | Delete a pod (Deployment recreates it) |
kubectl delete -f file.yaml | Remove objects defined in a manifest |
kubectl rollout restart deploy web | Restart all pods (e.g. after a new image) |
minikube service <name> | Open a NodePort service in the browser |
minikube dashboard | Open the visual cluster dashboard |
| Pod status | Likely cause & fix |
|---|---|
ErrImagePull / ImagePullBackOff | Cluster can't find the image — run minikube image load notes-app:1.0 and set imagePullPolicy: IfNotPresent. |
CrashLoopBackOff | App keeps crashing — check kubectl logs <pod>. Often the DB isn't reachable yet. |
Pending | No resources / cluster still starting — kubectl describe pod shows why. |
| Web can't reach the database | Service name or DATABASE_URL host must be db; check the Secret values. |
minikube service hangs | Leave the terminal open — it tunnels traffic; closing it closes the tunnel. |
| Changes not applied | Re-run kubectl apply -f k8s/; for a new image, kubectl rollout restart deploy web. |
Push your understanding before moving on:
web to 4 replicas, then run kubectl get pods -o wide to see them.minikube dashboard and watch a pod restart visually after you delete it.Run a local cluster, write Deployment/Service/Secret manifests, deploy a multi-tier app, and watch Kubernetes self-heal and scale it. That's the foundation of running software in production.
Next up: Module 9 — CI/CD, where you'll automate all of this — every git push builds your image, runs tests, and deploys it for you with GitHub Actions.