Docker taught you how to package an application into a container. Kubernetes is what you reach for once you have more than a handful of containers to keep running, restart when they crash, and scale when traffic spikes. If you haven’t worked with containers yet, start with Docker fundamentals first — Kubernetes assumes you already know what an image and a container actually are.
What Kubernetes Actually Does, Concretely
Instead of manually running docker run for each instance of your app and watching them yourself, you write a file describing what you want — how many copies, how they should recover from failure, how traffic should reach them — and Kubernetes continuously works to match reality to that description. If a pod crashes, Kubernetes notices and starts a replacement without you doing anything. That’s the whole model: you declare the desired state, Kubernetes reconciles toward it, constantly.
The Building Blocks, With Actual Syntax
A Pod is the smallest unit Kubernetes schedules — usually one container, sometimes a couple that need to share networking and storage. You rarely create pods directly in practice; you create a Deployment, which manages a set of identical pods for you and handles rolling updates. Here’s what a real, minimal Deployment actually looks like:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 3
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: web-app
image: yourrepo/web-app:1.4.2
ports:
- containerPort: 8080
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
Apply that with kubectl apply -f deployment.yaml, and Kubernetes creates three pods running that image. Check on them with kubectl get pods, and if you need to see why one is misbehaving, kubectl logs <pod-name> and kubectl describe pod <pod-name> are the two commands you’ll reach for constantly — describe shows you scheduling and health-check events, logs shows you what the application itself printed.
Pods are disposable and get new IP addresses whenever they restart, which is why a Service exists — it gives you one stable address that automatically routes to whichever pods are currently healthy, so the rest of your system doesn’t need to track pod churn itself.
ConfigMaps and Secrets exist so you’re not hardcoding a database URL or an API key into your container image — ConfigMaps for ordinary settings, Secrets (base64-encoded, and ideally backed by something like Sealed Secrets or your cloud provider’s secret manager in production, since base64 alone isn’t real encryption) for anything sensitive.
Local Development: Skip the “Lightweight Cluster” Vagueness
You don’t need a real cloud cluster to learn or develop against Kubernetes. Three real tools cover almost everyone’s local setup: kind (Kubernetes-in-Docker, fast, good for CI pipelines too), minikube (the longest-standing option, runs a full VM-backed cluster), and k3d, a lightweight wrapper around k3s that starts quickly and is popular for local dev specifically because of its low resource footprint. Docker Desktop also ships a built-in single-node Kubernetes option if you want the absolute lowest-friction start. Pick any one of them, run kubectl get nodes afterward to confirm it’s up, and you’re working against the same API you’d use in production.
Scaling: What Actually Triggers It
Horizontal scaling in Kubernetes is usually handled by a HorizontalPodAutoscaler, which watches a metric (commonly CPU) and adjusts your replica count automatically:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web-app
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
That configuration keeps you between 3 and 10 pods, scaling up once average CPU utilization crosses 70%. Getting the resource requests and limits right on your Deployment (shown earlier) matters more than people expect here — autoscaling decisions are based on those numbers, and setting them carelessly leads to either wasted capacity or pods getting killed for exceeding memory limits you set too low.
Storage, Briefly
Containers lose their filesystem when they’re replaced, which is fine for stateless apps and a real problem for databases. Kubernetes handles this with PersistentVolumes and PersistentVolumeClaims — a claim requests storage, a volume fulfills it, and the pod mounts it like a normal disk that survives pod restarts. For anything stateful in production, most teams reach for a managed database service outside the cluster entirely rather than running a database inside Kubernetes, specifically to avoid the operational complexity of managing stateful storage themselves.
The Piece Most Guides Skip: Helm and GitOps
Writing raw YAML for every deployment gets unwieldy fast once you have more than a couple of services. Helm is the de facto standard package manager for Kubernetes — it lets you template your manifests and install/upgrade an application with one command (helm install myapp ./chart) instead of managing a folder of YAML files by hand.
For getting changes into your cluster reliably, GitOps tools like ArgoCD or Flux watch a Git repository and automatically sync your cluster to match what’s committed — so a deployment becomes “merge this pull request” rather than someone manually running kubectl apply from their laptop. This is the actual modern CI/CD pattern most production Kubernetes setups use now, not just “build container, deploy, monitor” as a vague three-step loop.
Debugging When Things Go Wrong
If you’re troubleshooting a container that keeps crashing before you even get to Kubernetes-specific issues, this rundown of common Docker errors covers the layer underneath Kubernetes that’s worth ruling out first. Within Kubernetes itself, kubectl get events --sort-by=.lastTimestamp is an underused command that shows you a timeline of what the cluster actually did, which is often faster than guessing from logs alone.
When Kubernetes Genuinely Isn’t Worth It
If you’re running one or two services with predictable, low traffic, Kubernetes adds real operational overhead — a control plane to maintain, YAML to write and review, a genuinely steeper learning curve for your team — for benefits you may not need yet. A single VM with Docker Compose, or a managed platform like Cloud Run or App Runner, solves the same problem with far less complexity for a workload that isn’t actually distributed or scaling unpredictably. Kubernetes earns its complexity once you have multiple services, need to scale dynamically, or need the self-healing and rollout control it provides — not before.
Mastery here comes from actually running a cluster and breaking things in it, not from reading about the concepts. Spin up a local cluster with kind or minikube this week, deploy something real to it, and force a pod to crash on purpose just to watch Kubernetes replace it. That single exercise teaches more than any conceptual overview, this one included.