Kubernetes (also written k8s) is an open-source system for automating the deployment, scaling, and management of containerized applications. Originally built by Google and now maintained by the CNCF (Cloud Native Computing Foundation), Kubernetes is the de-facto standard for running containers in production.
If Docker lets you package an application into a container, Kubernetes answers the next question: how do you run hundreds of those containers reliably, keep them healthy, scale them up under load, and roll out updates without downtime?
Kubernetes in 30 seconds
| Concept | What it means |
|---|---|
| Pod | Smallest deployable unit — one or more containers sharing storage/network |
| Node | A physical or virtual machine that runs Pods |
| Cluster | A set of Nodes managed by Kubernetes |
| Deployment | Declares how many Pod replicas to run and how to update them |
| Service | A stable network endpoint (IP + DNS name) that routes traffic to Pods |
| Namespace | A virtual cluster inside a cluster — used for isolation/organisation |
| ConfigMap / Secret | Inject configuration and sensitive data into Pods |
| Ingress | HTTP(S) routing rules (like a reverse proxy) into the cluster |
# Check your cluster is reachable
kubectl cluster-info
# List all running Pods in default namespace
kubectl get pods
# Deploy a containerised app
kubectl apply -f deployment.yaml
# Scale to 5 replicas
kubectl scale deployment my-app --replicas=5
Why does Kubernetes exist?
Running one container on your laptop is easy. Running 50 microservices across 100 servers in production is a different problem entirely. Kubernetes solves:
| Problem | Kubernetes solution |
|---|---|
| Container crashes | Automatically restarts failed containers |
| Traffic spike | Auto-scales Pods up (or down) based on CPU/memory |
| Rolling updates | Replaces old Pods gradually, zero downtime |
| Service discovery | Built-in DNS so services find each other by name |
| Load balancing | Distributes traffic across healthy Pod replicas |
| Secret management | Stores passwords/tokens encrypted, injects into Pods |
| Multi-host scheduling | Places Pods on the best available Node automatically |
| Self-healing | Replaces or reschedules Pods on failed Nodes |
Kubernetes architecture
Kubernetes has two types of machines in a cluster: the Control Plane (the brain) and Worker Nodes (where your apps actually run).
┌─────────────────────────────────────────────────────────┐
│ CONTROL PLANE │
│ ┌────────────┐ ┌──────────┐ ┌────────────────────┐ │
│ │ API Server │ │Scheduler │ │Controller Manager │ │
│ └────────────┘ └──────────┘ └────────────────────┘ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ etcd (key-value store) │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
│ │ │
┌────────▼──┐ ┌───────▼──┐ ┌──────▼────┐
│ Worker │ │ Worker │ │ Worker │
│ Node 1 │ │ Node 2 │ │ Node 3 │
│ ┌───────┐ │ │ ┌──────┐ │ │ ┌───────┐ │
│ │ Pod A │ │ │ │ Pod B│ │ │ │ Pod A │ │
│ │ Pod B │ │ │ │ Pod C│ │ │ │ Pod D │ │
│ └───────┘ │ │ └──────┘ │ │ └───────┘ │
│ kubelet │ │ kubelet │ │ kubelet │
│ kube- │ │ kube- │ │ kube- │
│ proxy │ │ proxy │ │ proxy │
└───────────┘ └──────────┘ └───────────┘
Control Plane components
| Component | Role |
|---|---|
| API Server | The front door to Kubernetes — all kubectl commands go through here |
| etcd | Distributed key-value store that holds all cluster state |
| Scheduler | Decides which Node to place a new Pod on |
| Controller Manager | Runs control loops to keep the actual state matching the desired state |
| Cloud Controller Manager | Integrates with cloud provider APIs (AWS, GCP, Azure) |
Worker Node components
| Component | Role |
|---|---|
| kubelet | Agent on each Node that ensures Pods are running as expected |
| kube-proxy | Maintains network rules for Service routing |
| Container Runtime | Runs the actual containers (containerd, CRI-O) |
Core Kubernetes objects
Pod
The smallest unit you can deploy. Usually one container, sometimes a sidecar pattern (main container + helper).
apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
containers:
- name: app
image: nginx:1.25
ports:
- containerPort: 80
Pods are ephemeral — when they die, they're replaced. Never hardcode a Pod's IP address. Use a Service instead.
Deployment
The standard way to run a stateless application. Manages a ReplicaSet (set of identical Pods).
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3 # Run 3 copies
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app:v2.0
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
Key Deployment capabilities:
| Feature | How it works |
|---|---|
| Rolling update | Replaces old Pods one at a time — zero downtime by default |
| Rollback | kubectl rollout undo deployment/my-app reverts to previous version |
| Scale | kubectl scale deployment my-app --replicas=10 instantly |
| Self-healing | If a Pod crashes, Deployment recreates it to maintain replica count |
Service
A stable virtual IP + DNS name that routes traffic to Pods. Even when Pods are replaced (and get new IPs), the Service address never changes.
apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
selector:
app: my-app # Routes to all Pods with this label
ports:
- port: 80
targetPort: 8080
type: ClusterIP # Internal only (default)
Service types:
| Type | Accessible from | Use case |
|---|---|---|
| ClusterIP | Inside the cluster only | Internal microservice communication |
| NodePort | Outside via <NodeIP>:<port> |
Simple external access / dev |
| LoadBalancer | Cloud provider's external LB | Production public-facing services |
| ExternalName | DNS alias to external service | Pointing to an external database |
ConfigMap and Secret
Decouple configuration from container images.
# ConfigMap — non-sensitive config
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
DATABASE_URL: "postgres://db-service:5432/mydb"
LOG_LEVEL: "info"
---
# Secret — sensitive data (base64-encoded)
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
type: Opaque
stringData:
DATABASE_PASSWORD: "super-secret-password"
API_KEY: "sk_live_abcdef123456"
Inject into a Pod as environment variables:
spec:
containers:
- name: app
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: app-secrets
Ingress
Routes external HTTP(S) traffic to Services based on host/path rules — like a smart reverse proxy at the cluster edge.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: myapp.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: frontend-service
port:
number: 80
How Kubernetes handles updates
Zero-downtime rolling updates are built in by default:
v1 Pods: [●][●][●] 3 running
Rolling update starts (maxUnavailable: 1, maxSurge: 1):
Step 1: [●][●][●][○] 1 v2 Pod created
Step 2: [●][●][ ][○] 1 v1 Pod terminated
Step 3: [●][●][○][○] 1 v2 Pod created
Step 4: [●][ ][○][○] 1 v1 Pod terminated
Step 5: [○][○][○] All v2, update complete
● = v1 Pod ○ = v2 Pod
To rollback:
kubectl rollout history deployment/my-app # See revision history
kubectl rollout undo deployment/my-app # Rollback to previous
kubectl rollout undo deployment/my-app --to-revision=2 # Specific revision
Auto-scaling
Horizontal Pod Autoscaler (HPA)
Scales the number of Pods based on CPU/memory (or custom metrics):
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 2
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # Scale out when CPU > 70%
Vertical Pod Autoscaler (VPA)
Adjusts CPU/memory requests for existing Pods (right-sizes containers).
Cluster Autoscaler
Adds or removes Nodes from the cluster when Pods can't be scheduled (cloud only).
Namespaces: multi-tenancy in one cluster
Namespaces provide isolation between teams, environments, or projects:
# Create namespaces
kubectl create namespace staging
kubectl create namespace production
# Deploy to a specific namespace
kubectl apply -f deployment.yaml -n production
# List everything in a namespace
kubectl get all -n staging
Common namespace pattern:
| Namespace | Usage |
|---|---|
default |
Where resources land if no namespace is specified |
kube-system |
Kubernetes internal components |
kube-public |
Public cluster information |
monitoring |
Prometheus, Grafana |
staging / production |
Environment isolation |
Essential kubectl commands
# ── Cluster info ─────────────────────────────────────────
kubectl cluster-info
kubectl get nodes
kubectl get nodes -o wide # With IP addresses
# ── Pods ─────────────────────────────────────────────────
kubectl get pods
kubectl get pods -n kube-system # In a specific namespace
kubectl describe pod <name> # Detailed info + events
kubectl logs <pod-name> # Container logs
kubectl logs <pod-name> -f # Follow (tail -f)
kubectl exec -it <pod-name> -- bash # Shell into a running Pod
# ── Deployments ──────────────────────────────────────────
kubectl get deployments
kubectl apply -f deployment.yaml # Create or update
kubectl delete -f deployment.yaml # Delete
kubectl scale deployment my-app --replicas=5
kubectl rollout status deployment/my-app
kubectl rollout undo deployment/my-app
# ── Services ─────────────────────────────────────────────
kubectl get services
kubectl port-forward svc/my-service 8080:80 # Local port-forward
# ── Debugging ────────────────────────────────────────────
kubectl get events --sort-by='.lastTimestamp'
kubectl top pods # CPU/memory usage (needs metrics-server)
kubectl top nodes
kubectl describe node <node-name> # Node capacity + allocated resources
Kubernetes vs Docker: what's the difference?
A common point of confusion — Docker and Kubernetes are complementary, not alternatives:
| Docker | Kubernetes | |
|---|---|---|
| What it does | Builds and runs containers | Orchestrates containers across many machines |
| Scale | Single host | Multi-host clusters |
| Self-healing | No (you restart manually) | Yes (auto-restarts, reschedules) |
| Load balancing | Via docker-compose (basic) | Built-in Services + Ingress |
| Rolling updates | Manual | Built-in, zero-downtime |
| Auto-scaling | No | HPA / VPA / Cluster Autoscaler |
| Networking | Bridge network per compose project | Flat Pod network across all Nodes |
| Learning curve | Low | Steep |
| Best for | Local dev, simple single-server deploys | Production, microservices, cloud |
The simple mental model: Docker packages your app; Kubernetes runs it in production.
Managed Kubernetes options
You don't have to manage your own control plane. Cloud providers offer fully managed Kubernetes:
| Service | Provider | Notes |
|---|---|---|
| GKE (Google Kubernetes Engine) | Google Cloud | Most mature, auto-pilot mode |
| EKS (Elastic Kubernetes Service) | AWS | Largest cloud provider, most integrations |
| AKS (Azure Kubernetes Service) | Microsoft Azure | Strong enterprise/Windows container support |
| DigitalOcean Kubernetes | DigitalOcean | Simpler, cheaper, great for startups |
| Civo | Civo | Lightweight k3s-based, very fast provisioning |
| k3s | Rancher/SUSE | Lightweight self-hosted, great for edge/homelab |
The Kubernetes ecosystem
| Tool | Purpose |
|---|---|
| Helm | Package manager for Kubernetes (like apt/npm for k8s) |
| Kustomize | Template-free YAML customisation |
| Argo CD | GitOps continuous deployment |
| Flux | GitOps alternative to Argo CD |
| Prometheus + Grafana | Monitoring and dashboards |
| Istio / Linkerd | Service mesh (mTLS, traffic management, observability) |
| Cert-Manager | Automatic TLS certificates (Let's Encrypt) |
| Karpenter | Intelligent Node provisioning on AWS |
| Velero | Cluster backup and restore |
| OPA / Gatekeeper | Policy enforcement |
| Lens | Kubernetes IDE / desktop GUI |
| k9s | Terminal UI for Kubernetes |
When to use Kubernetes — and when not to
Use Kubernetes when:
- Running microservices that need independent scaling
- You need zero-downtime deployments out of the box
- Your team is operating at scale (many services, many instances)
- You need sophisticated scheduling (GPU nodes, spot instances, etc.)
- You want GitOps / IaC-driven infrastructure
- Running on managed cloud (GKE/EKS/AKS removes most ops burden)
Don't use Kubernetes when:
| Situation | Better alternative |
|---|---|
| Single small app | Docker Compose + a cheap VPS |
| Simple static site | Vercel, Netlify, GitHub Pages |
| One or two APIs | Railway, Render, Fly.io |
| Very early-stage startup | The overhead isn't worth it yet |
| Tiny team with no k8s experience | Learning curve > benefit until you need scale |
| Serverless workloads | AWS Lambda, Google Cloud Run |
Rule of thumb: If you're asking "do I need Kubernetes?", you probably don't yet. When you genuinely feel the pain of managing containers at scale, Kubernetes becomes the obvious answer.
Kubernetes compared to alternatives
| Kubernetes | Docker Swarm | Nomad | ECS (AWS) | Cloud Run | |
|---|---|---|---|---|---|
| Complexity | High | Low | Medium | Medium | Low |
| Ecosystem | Massive | Small | Medium | AWS-only | GCP-only |
| Multi-cloud | Yes | Yes | Yes | No | No |
| Auto-scaling | Yes (HPA) | Limited | Yes | Yes | Yes |
| Service mesh | Istio, Linkerd | No | Consul Connect | App Mesh | No |
| Stateful apps | StatefulSets | Limited | Yes | EFS | No |
| Best for | Large-scale microservices | Simple swarms | Mixed workloads | AWS-native | Serverless containers |
Common Kubernetes mistakes
| Mistake | Better approach |
|---|---|
No resource limits set on containers |
Always set requests and limits; prevents one Pod from starving others |
| Running as root inside containers | Add securityContext: runAsNonRoot: true |
| Storing secrets in ConfigMaps | Use Secrets (or External Secrets Operator + Vault) |
| One giant monolith namespace | Separate namespaces per team/environment |
| Skipping liveness/readiness probes | K8s can't detect stuck apps without them |
Hard-coding image tags as latest |
Pin to specific digest or semver tag |
| No Pod Disruption Budget | Deployments can have all Pods evicted at once during node drain |
| No network policies | All Pods can talk to all Pods by default — tighten with NetworkPolicy |
Frequently asked questions
Is Kubernetes only for large companies? No — but the operational overhead makes more sense at scale. Managed services (GKE Autopilot, EKS, AKS) dramatically reduce that overhead. Startups often adopt Kubernetes earlier than they should; many find a simple Fly.io or Railway setup better until they outgrow it.
Do I need to know Docker before Kubernetes? Yes. Kubernetes orchestrates containers, so you need to understand what a container is, how to write a Dockerfile, and how images/registries work first.
What language is Kubernetes written in? Go (Golang). Most of the Kubernetes ecosystem tooling (kubectl, Helm, etc.) is also written in Go.
What is a "manifest" in Kubernetes?
A YAML (or JSON) file describing a Kubernetes object — Pod, Deployment, Service, etc. You apply manifests with kubectl apply -f.
What is Helm?
Helm is the package manager for Kubernetes. A "Helm chart" is a reusable, parameterised package of Kubernetes manifests. Instead of maintaining 10 raw YAML files for Postgres, you run helm install postgres bitnami/postgresql --set auth.password=secret.
How is Kubernetes different from serverless? Serverless (Lambda, Cloud Functions) abstracts away servers completely — you deploy functions, not containers. Kubernetes gives you containers (more control, more operational responsibility). Cloud Run bridges the gap: containerised apps with serverless-style auto-scaling.
Quick start: local Kubernetes
The easiest ways to run Kubernetes locally:
# Option 1: Docker Desktop (enable Kubernetes in Settings)
# Built in — no install needed if you have Docker Desktop
# Option 2: minikube
brew install minikube # macOS
minikube start
kubectl get nodes
# Option 3: kind (Kubernetes IN Docker)
go install sigs.k8s.io/kind@latest
kind create cluster
kubectl cluster-info --context kind-kind
# Option 4: k3d (k3s in Docker)
brew install k3d
k3d cluster create mycluster
kubectl get nodes
Deploy your first app:
# Create a Deployment
kubectl create deployment hello-k8s --image=nginx:1.25
# Expose it as a Service
kubectl expose deployment hello-k8s --port=80 --type=NodePort
# Open in browser (minikube)
minikube service hello-k8s
# Or port-forward manually
kubectl port-forward svc/hello-k8s 8080:80
# Visit http://localhost:8080
Kubernetes has a steep learning curve — but once it clicks, it fundamentally changes how you think about running software in production. The concepts (desired state, self-healing, declarative config) apply well beyond Kubernetes itself.