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: 256Mi
kubectl 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
FieldMeaning
replicasHow many identical pods should exist
selectorWhich pods this Deployment manages, matched by label
templateThe pod definition stamped out for each replica
maxSurgeExtra pods allowed above the desired count during an update
maxUnavailablePods 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 it
kubectl 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.

ConfigMaps, Secrets and probes Rollouts and debugging

Last refreshed 2026-09-18.