Pods, Deployments and Services
The three objects every workload is built from, and why the indirection between them exists.
Pods
The Pod is the smallest schedulable unit. Its containers share one network namespace, so they reach each other on localhost, and they can share volumes. Most pods hold exactly one application container.
apiVersion: v1
kind: Pod
metadata:
name: web
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.27-alpine
ports:
- containerPort: 80 # documentation only; it publishes nothing
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
memory: 256Mikubectl apply -f pod.yaml
kubectl get pods -o wide
kubectl describe pod web
kubectl delete pod web- A bare Pod is not rescheduled if its node dies, and it is not restarted if the process exits. Use a controller such as a Deployment for anything real.
- Every pod gets its own cluster IP, and that address changes whenever the pod is recreated. Never hard-code it.
- Everything is declarative: you write the desired state and controllers work to make reality match.
Deployments
A Deployment owns a ReplicaSet, which owns Pods. You declare the desired replica count and image, and Kubernetes creates and replaces pods to match while handling rolling updates for you.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web # must match the pod template labels
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.27-alpine
ports:
- containerPort: 80| Field | Meaning |
|---|---|
replicas | How many identical pods should exist |
selector | Which pods this Deployment manages, matched by label |
template | The pod definition stamped out for each replica |
maxSurge | Extra pods allowed above the desired count during an update |
maxUnavailable | Pods allowed to be down during an update |
Services
Pods come and go, so clients need a stable address. A Service provides one virtual IP and DNS name, and load balances across whichever pods currently match its selector.
apiVersion: v1
kind: Service
metadata:
name: web
spec:
type: ClusterIP # ClusterIP | NodePort | LoadBalancer
selector:
app: web
ports:
- port: 80 # the port clients of the Service use
targetPort: 80 # the container port behind itkubectl get svc
kubectl get endpoints web # an empty list means the selector matches nothing
kubectl port-forward svc/web 8080:80
# other pods address it by DNS name:
# http://web.<namespace>.svc.cluster.local💡
When a Service returns nothing, check
kubectl get endpoints first. An empty endpoint list almost always means a label typo or a readiness probe that never passes, not a networking problem.FAQ
The Deployment is ready but the app is unreachable?
Check
kubectl get endpoints <service>. If it is empty, the Service selector does not match the pod labels or the pods are not Ready. Fix the labels or the readiness probe.ClusterIP, NodePort or LoadBalancer?
ClusterIP for traffic inside the cluster, which is the default. NodePort to expose a port on every node. LoadBalancer to request an external address from the cloud provider, though an Ingress controller is usually the better answer.
Related
ConfigMaps, Secrets and probes Rollouts and debugging
Last refreshed 2026-09-18.