on
Pods, Deployments, and Services: a friendly guide to how Kubernetes runs your app
Kubernetes can feel like a new language at first, but three concepts map tightly to real-world roles: Pods are the actual workers, Deployments are the manager who hires and replaces them, and Services are the receptionist who routes visitors to the right worker. This article walks through those three building blocks in simple terms, shows short example YAML, and points out the common gotchas beginners run into.
Why these three matter
If you think of an application as a small band: the musicians are containers, the band plays gigs (runs the app), someone organizes who’s on stage and when (the Deployment), and a promoter or venue directs the audience to the performance (the Service). Kubernetes orchestrates all of that across a cluster of machines.
Understanding Pods, Deployments, and Services is the fastest way to go from “I can run one container locally” to “I can run a resilient, reachable app in a cluster.”
Pods: the smallest deployable unit
A Pod is the smallest object you create in Kubernetes. It packages one or more containers that should run together and share the same network namespace and storage. In practice, most Pods hold a single container; multiple containers in a Pod are for tight pairs (sidecars) that must share resources directly. Pods are ephemeral—their IPs and lifecycle are managed by Kubernetes and they may be replaced at any time. (kubernetes.io)
Short analogies:
- Pod = one apartment unit (containers are roommates who share utilities).
- Pod IP = the apartment’s address; if the apartment gets demolished, the next one may have a different address.
Example (minimal Pod YAML):
apiVersion: v1
kind: Pod
metadata:
name: hello-pod
spec:
containers:
- name: hello
image: nginx:latest
ports:
- containerPort: 80
This is fine for quick experiments or debugging, but not ideal for production (see Deployments).
Deployments: declare the desired state, let Kubernetes reconcile it
A Deployment manages a ReplicaSet, which in turn ensures the desired number of Pod replicas are running. Deployments give you:
- Declarative updates (you tell Kubernetes the desired state, e.g., “3 replicas of version X”).
- Rolling updates and rollbacks.
- Automatic replacement of failed Pods and rescheduling to healthy nodes.
Because Deployments automate healing and upgrades, they are the recommended way to run stateless services in most environments. Think of a Deployment as the band manager who hires the right number of musicians, replaces them if someone’s sick, and updates the lineup when the band releases a new song. (kubernetes.io)
Deployment example (stateless web app):
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-deployment
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.25
ports:
- containerPort: 80
Notes:
- The Deployment’s pod template is essentially a Pod spec. You rarely create standalone Pods for production—use a Deployment so Kubernetes can manage lifecycle and scaling for you.
Services: stable networking to ephemeral Pods
Because Pods are ephemeral and their IPs can change, Services provide a stable network front door to a set of Pods selected by labels. A Service gives you a stable name and IP, and forwards traffic to whatever Pods match its selector. That indirection is essential for reliability and load balancing inside the cluster. (kubernetes.io)
Common Service types beginners encounter:
- ClusterIP (default): Provides an internal IP only reachable from inside the cluster.
- NodePort: Exposes a port on each node so external traffic can reach the Service via
: . - LoadBalancer: Requests a cloud provider load balancer and exposes the Service externally through it (built on top of NodePort/ClusterIP behavior). (docs.cloud.google.com)
Example (ClusterIP Service pointing at the Deployment above):
apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
selector:
app: web
ports:
- port: 80
targetPort: 80
type: ClusterIP
Key fields:
- selector: determines which Pods receive traffic.
- port: the Service port (what clients talk to).
- targetPort: the port on the Pod/container to forward to.
How they work together (a minimal workflow)
- You create a Deployment with a Pod template and label (e.g., app=web).
- Kubernetes creates a ReplicaSet and the requested Pods.
- You create a Service that selects app=web. The Service gets a stable ClusterIP and will route traffic to any matching Pod.
- When you scale the Deployment or update the image, Kubernetes orchestrates rolling replacements and the Service automatically routes to the new Pods.
This decoupling (Service from Pod identity) is what lets Kubernetes scale and self-heal without breaking connectivity.
Common beginner pitfalls (and how to think about them)
- Creating a single Pod for production: single Pods don’t self-heal. Use Deployments to get resilience. (kubernetes.io)
- Label selectors that don’t match: Services won’t route traffic if labels in the Deployment/Pods don’t match the Service selector. Always double-check label names and spellings.
- Confusing port vs targetPort: port is what callers use; targetPort is what the container listens on. Mismatches mean traffic arrives but nobody answers.
- Expecting LoadBalancer to work everywhere: LoadBalancer Services rely on cloud-provider integration or extra components (MetalLB for bare metal). If you’re on a local cluster (minikube, kind), you may need special steps. (docs.cloud.google.com)
- Trying to expose many services with NodePort: NodePort opens ports on every node (range typically 30000–32767); it’s useful for testing but not scalable for production-facing HTTP routing. Ingress or Gateway APIs are the usual abstraction for HTTP hosting.
When to use Ingress (or the newer Gateway API)
If you need HTTP routing, SSL termination, or multiple host/path routes through one external endpoint, Ingress is the usual next step—Ingress maps HTTP(S) traffic to Services using rules and an Ingress controller. Note that the Kubernetes project recommends the Gateway API as the long-term evolution of Ingress, but Ingress remains widely used and stable. (kubernetes.io)
Think of Ingress as the venue’s box office that can route different audiences to different stages (Services) based on the URL or hostname.
Short cheatsheet
- Pod: one or more containers that share network/storage; ephemeral. (kubernetes.io)
- Deployment: declarative controller for managing Pod replicas, updates, and rollbacks. (kubernetes.io)
- Service: stable network front-door to Pods; types include ClusterIP, NodePort, LoadBalancer. (kubernetes.io)
- Ingress/Gateway: HTTP/HTTPS routing into Services (Ingress stable, Gateway API recommended going forward). (kubernetes.io)
Final tip: practice with a small app
A simple pattern to try on a local cluster (minikube, kind) or a free cloud sandbox:
- Create a Deployment for a small web app (2–3 replicas).
- Create a ClusterIP Service and confirm it works with kubectl port-forward or a curl from a pod.
- Try changing the Deployment image to exercise rolling updates.
- Swap the Service to LoadBalancer on a cloud cluster (or add Ingress) to see external routing.
These steps build muscle memory for how the pieces fit together. Kubernetes looks complicated from a distance, but once you see the roles each object plays—worker (Pod), manager (Deployment), receptionist (Service)—the system becomes much more approachable.
Happy exploring—and bring your curiosity (and a playlist) when you try your first rolling update.