Part ofKubernetes Foundations·Step 1 of 4
DevOps & Platform

Kubernetes Core Concepts: Pods, Deployments & Services

Beginner60 min to complete18 min readJune 1, 2026Updated August 19, 2026

Quick answer

Learn how Kubernetes works — the cluster model, Pods, Deployments, Services, Namespaces, and the kubectl commands you'll use every day. If you know Docker Compose, you already know the mental model.

beginner · 60 min

Before you begin

  • Docker fundamentals — images, containers, Dockerfile
  • Docker Compose — services, ports, environment variables
  • kubectl installed and a local cluster (Docker Desktop K8s, kind, or minikube)
Kubernetes
K8s
Containers
DevOps
Platform Engineering

Kubernetes Core Concepts: Pods, Deployments & Services

If you've worked through the Docker and Docker Compose tutorials, you already have the mental model. Kubernetes extends the same concepts across a cluster of machines. Everything in Compose has a direct equivalent:

Docker ComposeKubernetes
ContainerPod
services: entryDeployment + Service
Service name DNSService ClusterIP DNS
Named volumePersistentVolumeClaim
.env / env_fileConfigMap + Secret
depends_on: condition: service_healthyreadinessProbe
docker compose upkubectl apply -f
docker compose downkubectl delete -f

The key difference: Compose runs everything on one machine. Kubernetes runs workloads across a cluster of nodes, scheduling them automatically, restarting them on failure, and routing traffic between them.


How a Cluster Works

A Kubernetes cluster has two roles:

Control plane — the cluster's brain (usually 3 nodes for high availability):

  • kube-apiserver — the single entry point; every kubectl command talks to this REST API
  • etcd — key-value store; the source of truth for all cluster state
  • kube-scheduler — watches for unscheduled Pods and assigns them to nodes
  • kube-controller-manager — runs control loops (Deployment controller, Node controller, etc.)

Worker nodes — where your workloads run:

  • kubelet — agent on each node; ensures Pods described by specs are actually running
  • kube-proxy — maintains network rules for Service routing (iptables/IPVS)
  • Container runtime — containerd or CRI-O (Docker daemon was removed in K8s 1.24)

Docker images still work. Kubernetes speaks the OCI image format, which Docker produces. Only the runtime — the thing that actually runs containers — changed.


Setting Up a Local Cluster

bash
1# kubectl — the Kubernetes CLI
2brew install kubectl                     # macOS
3# Linux: follow https://kubernetes.io/docs/tasks/tools/
4
5# Local cluster options (pick one)
6# Docker Desktop — enable Kubernetes in Settings → Kubernetes
7kind create cluster --name dev           # kind — Kubernetes IN Docker (recommended for CI)
8minikube start                           # minikube — full local cluster with add-ons
9
10# Verify
11kubectl cluster-info
12kubectl get nodes

Pods — The Atomic Unit

A Pod is one or more containers that:

  • Share the same network namespace (same IP address, communicate via localhost)
  • Can share volumes
  • Are always scheduled on the same node

In practice, most Pods run a single container. The multi-container pattern (sidecars) is for helpers that need to share the same localhost — a log shipper, a proxy, a config reloader.

yaml
1# pod.yaml
2apiVersion: v1
3kind: Pod
4metadata:
5  name: nginx
6  labels:
7    app: nginx
8spec:
9  containers:
10    - name: nginx
11      image: nginx:1.27-alpine
12      ports:
13        - containerPort: 80
14      resources:
15        requests:
16          cpu: "100m"     # 100 millicores = 0.1 CPU
17          memory: "64Mi"
18        limits:
19          cpu: "200m"
20          memory: "128Mi"
bash
kubectl apply -f pod.yaml
kubectl get pods
kubectl describe pod nginx     # Full details — events, node, IP, container state
kubectl logs nginx
kubectl exec -it nginx -- sh   # Alpine uses sh, not bash
kubectl delete pod nginx

You rarely create bare Pods directly. If the node a bare Pod runs on dies, the Pod is gone — nothing reschedules it. Use a Deployment instead.


Deployments — Desired State

A Deployment tells Kubernetes: "I want N replicas of this Pod running at all times." Kubernetes continuously reconciles actual state toward desired state.

yaml
1# deployment.yaml
2apiVersion: apps/v1
3kind: Deployment
4metadata:
5  name: api
6spec:
7  replicas: 3
8  selector:
9    matchLabels:
10      app: api           # Must match template labels
11  template:
12    metadata:
13      labels:
14        app: api
15    spec:
16      containers:
17        - name: api
18          image: nginx:1.27-alpine
19          ports:
20            - containerPort: 80
21          resources:
22            requests:
23              cpu: "100m"
24              memory: "128Mi"
25            limits:
26              cpu: "500m"
27              memory: "256Mi"
28          readinessProbe:
29            httpGet:
30              path: /health
31              port: 80
32            initialDelaySeconds: 5
33            periodSeconds: 10
34          livenessProbe:
35            httpGet:
36              path: /health
37              port: 80
38            initialDelaySeconds: 15
39            periodSeconds: 20
40  strategy:
41    type: RollingUpdate
42    rollingUpdate:
43      maxSurge: 1         # Create 1 extra pod before removing old ones
44      maxUnavailable: 0   # Never drop below desired replica count

Probes

ProbeFailure effectUse case
readinessProbePod removed from Service endpoints (not restarted)App is starting up or temporarily overloaded
livenessProbeContainer restartedApp is deadlocked and needs to be killed
startupProbeGates liveness/readiness until it passesSlow-starting apps (JVM, database migrations)

Rolling updates and rollbacks

bash
1# Update the image — triggers a rolling update
2kubectl set image deployment/api api=nginx:1.28-alpine
3
4# Watch the rollout progress
5kubectl rollout status deployment/api
6
7# Roll back to the previous version
8kubectl rollout undo deployment/api
9
10# See rollout history
11kubectl rollout history deployment/api
12
13# Roll back to a specific revision
14kubectl rollout undo deployment/api --to-revision=2
15
16# Scale manually
17kubectl scale deployment/api --replicas=5

Services — Stable Endpoints

Pods are ephemeral — they restart, reschedule, and get new IP addresses. A Service gives a stable DNS name and virtual IP (the ClusterIP) that load-balances across matching Pods.

Services find Pods using label selectors. Any Pod with app: api is automatically included in the Service's endpoint list.

yaml
1# service.yaml
2apiVersion: v1
3kind: Service
4metadata:
5  name: api
6spec:
7  selector:
8    app: api            # Matches any Pod with this label
9  ports:
10    - protocol: TCP
11      port: 80          # Port the Service listens on
12      targetPort: 80    # Port on the Pod
13  type: ClusterIP       # Default — only reachable within the cluster

Inside the cluster, api is reachable at:

  • api (same namespace)
  • api.default (cross-namespace short form)
  • api.default.svc.cluster.local (fully qualified DNS name)

Service types

TypeReachable fromUse case
ClusterIPInside cluster onlyMicroservice-to-microservice (default)
NodePortOutside, via <NodeIP>:<Port> (30000–32767)Dev/testing without a cloud LB
LoadBalancerOutside, via cloud LB IPProduction external traffic (EKS, GKE, AKS)
ExternalNameDNS alias to an external hostnamePoint db to db.rds.amazonaws.com
yaml
1# LoadBalancer — cloud provider creates an external LB
2spec:
3  type: LoadBalancer
4  selector:
5    app: api
6  ports:
7    - port: 80
8      targetPort: 80

Namespaces — Logical Isolation

Namespaces partition resources within a cluster. They're organisational, not security boundaries (for traffic isolation use NetworkPolicies).

bash
1kubectl get namespaces
2# default       — where resources go when you don't specify -n
3# kube-system   — Kubernetes system components (DNS, kube-proxy, etc.)
4# kube-public   — publicly readable cluster info
5
6kubectl create namespace staging
7kubectl apply -f deployment.yaml -n staging
8kubectl get pods -n staging
9kubectl get pods -A                          # All namespaces

Set a default namespace so you don't type -n on every command:

bash
kubectl config set-context --current --namespace=staging

Essential kubectl Commands

bash
1# Listing resources
2kubectl get pods
3kubectl get pods -o wide                   # Include node and IP columns
4kubectl get deployments
5kubectl get services
6kubectl get all                            # Pods, deployments, services, replicasets
7
8# Inspecting
9kubectl describe pod <name>                # Full details including events
10kubectl describe deployment <name>
11kubectl logs <pod>
12kubectl logs -f <pod>                      # Follow live
13kubectl logs <pod> -c <container>          # Specific container in multi-container pod
14kubectl logs --previous <pod>              # Logs from the last terminated container
15
16# Executing
17kubectl exec -it <pod> -- bash
18kubectl exec -it <pod> -- sh               # Alpine images
19kubectl exec <pod> -- env                  # Print environment variables
20
21# Applying and deleting
22kubectl apply -f manifest.yaml             # Create or update (idempotent)
23kubectl delete -f manifest.yaml
24kubectl delete pod <name>
25kubectl delete deployment <name>
26
27# Debugging
28kubectl get events --sort-by=.metadata.creationTimestamp
29kubectl port-forward pod/<name> 8080:80    # Forward local port → pod port
30kubectl port-forward service/<name> 8080:80
31kubectl top pods                           # CPU/memory (requires metrics-server)
32kubectl top nodes

The Reconciliation Loop

Kubernetes' core pattern: declare desired state, Kubernetes makes it happen.

You write YAML describing what you want. Kubernetes continuously watches the actual state of the cluster and acts to make it match:

  • A node dies → Pods are rescheduled on remaining nodes
  • A Pod crashes → the Deployment controller starts a replacement
  • You update an image → a rolling update runs automatically
  • You delete a Pod manually → the Deployment creates a new one immediately

This is fundamentally different from imperative container management (docker run, docker stop). In Kubernetes you never manage individual containers — you manage desired state.


A Complete Example

yaml
1# app.yaml — Deployment + Service
2apiVersion: apps/v1
3kind: Deployment
4metadata:
5  name: api
6spec:
7  replicas: 2
8  selector:
9    matchLabels:
10      app: api
11  template:
12    metadata:
13      labels:
14        app: api
15    spec:
16      containers:
17        - name: api
18          image: nginx:1.27-alpine
19          ports:
20            - containerPort: 80
21          resources:
22            requests:
23              cpu: "100m"
24              memory: "64Mi"
25            limits:
26              cpu: "200m"
27              memory: "128Mi"
28---
29apiVersion: v1
30kind: Service
31metadata:
32  name: api
33spec:
34  selector:
35    app: api
36  ports:
37    - port: 80
38      targetPort: 80
39  type: ClusterIP
bash
kubectl apply -f app.yaml
kubectl get pods
kubectl get service api
kubectl port-forward service/api 8080:80
curl http://localhost:8080

Frequently Asked Questions

Why do I need a Deployment rather than creating Pods directly?

A bare Pod is not recreated if its node fails or it is deleted — there is nothing watching it. A Deployment maintains a desired replica count, handles rolling updates and lets you roll back. Create Pods directly only for genuinely one-off debugging.

What actually happens when I apply a manifest?

The API server validates and stores the desired state in etcd. Controllers notice the difference between desired and actual and act — the deployment controller creates a ReplicaSet, which creates Pods, and the scheduler assigns them to nodes. Nothing is executed imperatively; everything is reconciled.

When should I use a namespace?

To separate environments, teams or tenants within a cluster, and as the boundary for quotas, RBAC and network policy. Namespaces are not a security boundary on their own — they are the unit those controls apply to. A handful of related services does not need one each.

What is the difference between a Service and an Ingress?

A Service gives a stable address for a set of Pods, mostly for traffic inside the cluster. An Ingress routes external HTTP traffic to Services by host and path, and needs a controller to do anything. Service for connectivity, Ingress for the front door.

What's Next

These tutorials are part of the Kubernetes Foundations learning path — Stage 3 of the Platform Engineering Roadmap.

Next in Kubernetes Foundations

Kubernetes Networking & Ingress

Continue

We built Podscape to simplify Kubernetes workflows like this — logs, events, and cluster state in one interface, without switching tools.

Struggling with this in production?

We help teams fix these exact issues. Our engineers have deployed these patterns across production environments at scale.