You’ve containerized your app with Docker. It works great on one server. Now you need it on ten servers, with automatic scaling, rolling updates, and self-healing. Welcome to Kubernetes.

What Kubernetes Actually Does

Kubernetes (K8s) is a container orchestrator. You tell it what you want — “run 3 copies of my app, keep them healthy, expose them to the internet” — and it figures out how to make that happen.

Think of it as a very capable operations team that never sleeps:

  • Scheduling: Decides which server runs which container
  • Scaling: Adds or removes containers based on demand
  • Healing: Restarts crashed containers automatically
  • Networking: Routes traffic to healthy containers
  • Updates: Rolls out new versions without downtime

Core Concepts

Pods

The smallest unit in Kubernetes. A pod is one or more containers that share storage and network.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
apiVersion: v1
kind: Pod
metadata:
  name: my-app
spec:
  containers:
    - name: app
      image: myapp:1.0
      ports:
        - containerPort: 8080

Most of the time, you don’t create pods directly — you use Deployments.

Deployments

Manages pods for you: how many replicas, how to update them, how to roll back.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: app
          image: myapp:1.0
          ports:
            - containerPort: 8080
          resources:
            requests:
              memory: "128Mi"
              cpu: "100m"
            limits:
              memory: "256Mi"
              cpu: "500m"

Apply it:

1
kubectl apply -f deployment.yaml

Kubernetes creates 3 pods, distributes them across nodes, and keeps them running.

Services

Pods come and go. Services provide a stable endpoint to reach them.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
apiVersion: v1
kind: Service
metadata:
  name: my-app-service
spec:
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 8080
  type: ClusterIP

Service types:

  • ClusterIP: Internal only (default)
  • NodePort: Exposes on each node’s IP
  • LoadBalancer: Creates cloud load balancer

Ingress

Routes external HTTP traffic to services:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
spec:
  rules:
    - host: myapp.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-app-service
                port:
                  number: 80

Practical Example: Full Stack App

Let’s deploy a web app with a database.

ConfigMap for Configuration

1
2
3
4
5
6
7
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  DATABASE_HOST: "postgres-service"
  DATABASE_NAME: "myapp"

Secret for Sensitive Data

1
2
3
kubectl create secret generic db-credentials \
  --from-literal=username=admin \
  --from-literal=password=secretpass123

PostgreSQL Deployment

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:15
          env:
            - name: POSTGRES_USER
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: username
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: password
          ports:
            - containerPort: 5432
          volumeMounts:
            - name: postgres-storage
              mountPath: /var/lib/postgresql/data
      volumes:
        - name: postgres-storage
          persistentVolumeClaim:
            claimName: postgres-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: postgres-service
spec:
  selector:
    app: postgres
  ports:
    - port: 5432

Application Deployment

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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: app
          image: myapp:1.0
          envFrom:
            - configMapRef:
                name: app-config
          env:
            - name: DATABASE_USER
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: username
            - name: DATABASE_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: password
          ports:
            - containerPort: 8080
          readinessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 15
            periodSeconds: 20

Essential kubectl Commands

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Get resources
kubectl get pods
kubectl get deployments
kubectl get services
kubectl get all

# Describe (detailed info)
kubectl describe pod my-app-xyz123

# Logs
kubectl logs my-app-xyz123
kubectl logs -f my-app-xyz123  # Follow

# Execute commands in pod
kubectl exec -it my-app-xyz123 -- /bin/sh

# Scale
kubectl scale deployment my-app --replicas=5

# Update image
kubectl set image deployment/my-app app=myapp:2.0

# Rollback
kubectl rollout undo deployment/my-app

# Delete
kubectl delete -f deployment.yaml

When to Use Kubernetes

Good fit:

  • Multiple services that need to communicate
  • Variable load requiring auto-scaling
  • Teams deploying independently
  • Need for rolling updates and rollbacks
  • Multi-cloud or hybrid deployments

Overkill for:

  • Single application on one server
  • Small teams without ops capacity
  • Applications that rarely change
  • When managed services (Lambda, App Engine) work

Getting Started Locally

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# Install minikube
brew install minikube  # macOS
# or
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube

# Start cluster
minikube start

# Use kubectl
kubectl get nodes

# Deploy something
kubectl create deployment hello --image=nginx
kubectl expose deployment hello --port=80 --type=NodePort
minikube service hello

The Bottom Line

Kubernetes is complex because distributed systems are complex. But the abstraction it provides — “run my containers, keep them healthy, handle the networking” — is powerful.

Start small. Deploy one app. Learn the basics. The complexity is there when you need it, but you don’t have to use it all on day one.


Questions about Kubernetes? Find me on Twitter.