Skip to content

Kubernetes Pods, Deployments, and Services — Core Concepts with YAML Manifests and Rolling Updates

DodaTech Updated 2026-06-22 8 min read

In this tutorial, you'll learn about Kubernetes Pods, Deployments, and Services. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

Kubernetes is a container Orchestration platform that automates deployment, scaling, and management of containerized applications using declarative configurations and a powerful controller loop.

What You'll Learn

Why It Matters

Running containers on a single host with Docker works for development but breaks in production — containers crash, hosts fail, traffic spikes, and updates require zero downtime. Kubernetes solves these problems by giving you a self-healing, scalable platform where you describe the desired state and the system continuously works to match it.

Real-World Use

DodaTech runs Durga Antivirus Pro's backend services on a Kubernetes cluster with multiple Deployments (API servers, workers, scheduled jobs), Services for internal and external traffic routing, and ConfigMaps for environment-specific configuration — all managed through Git-versioned YAML manifests.

flowchart TD
    subgraph User
        A[Browser]
    end
    subgraph "Kubernetes Cluster"
        B[Ingress / Service]
        C[Deployment: API]
        D[Deployment: Worker]
        E[Deployment: CronJob]
        F[StatefulSet: Postgres]
        G[ConfigMap]
        H[Secret]
    end
    A --> B
    B --> C
    C --> F
    C --> G
    C --> H
    D --> F
    D --> G
    E --> F
    style C fill:#326CE5,color:#fff
    style D fill:#326CE5,color:#fff
    style E fill:#326CE5,color:#fff
â„šī¸ Info

Prerequisites: Basic Docker and Containerization knowledge. A running Kubernetes cluster (minikube, kind, or Docker Desktop) for practice.

Pods — The Smallest Unit

A Pod is the smallest deployable unit in Kubernetes — one or more containers that share a network namespace, storage volumes, and lifecycle.

# pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: web-pod
  labels:
    app: web
    tier: frontend
spec:
  containers:
    - name: nginx
      image: nginx:1.25-alpine
      ports:
        - containerPort: 80
      resources:
        requests:
          memory: "128Mi"
          cpu: "100m"
        limits:
          memory: "256Mi"
          cpu: "200m"
      readinessProbe:
        httpGet:
          path: /
          port: 80
        initialDelaySeconds: 3
        periodSeconds: 5

Expected behavior: Kubernetes schedules this Pod on a healthy node. The kubelet pulls the nginx image, starts the container, and begins the readiness probe checks. The Pod is ready to serve traffic only after the probe succeeds.

# Create the Pod
kubectl apply -f pod.yaml

# Expected output:
# pod/web-pod created

# Check Pod status
kubectl get pods -w

# Expected output:
# NAME      READY   STATUS    RESTARTS   AGE
# web-pod   0/1     Pending   0          5s
# web-pod   0/1     ContainerCreating   0          10s
# web-pod   1/1     Running   0          15s

Expected output: The Pod progresses from Pending (scheduling) to ContainerCreating (image pull) to Running (ready). The -w flag watches for changes in real time.

Deployments — Declarative Updates

A Deployment manages a ReplicaSet that maintains the desired number of Pod replicas. It handles rolling updates, rollbacks, and self-healing.

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
        - name: api
          image: myapp/api:1.0.0
          ports:
            - containerPort: 8080
          env:
            - name: DB_HOST
              value: postgres-service
            - name: DB_PORT
              value: "5432"
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /ready
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 5

Expected behavior: The Deployment creates 3 Pods matching the label selector app: api. If a Pod crashes, the Deployment replaces it immediately. Each Pod has health probes — liveness checks if the app is alive, readiness checks if it can serve traffic.

# Deploy the application
kubectl apply -f deployment.yaml

# Expected output:
# deployment.apps/api-deployment created

# Watch the rollout
kubectl rollout status deployment/api-deployment

# Expected output:
# Waiting for deployment "api-deployment" rollout to finish: 0 of 3 updated replicas...
# Waiting for deployment "api-deployment" rollout to finish: 1 of 3 updated replicas...
# deployment "api-deployment" successfully rolled out

# Perform a rolling update
kubectl set image deployment/api-deployment api=myapp/api:1.1.0

# Expected output:
# deployment.apps/api-deployment image updated

# Roll back if something goes wrong
kubectl rollout undo deployment/api-deployment

# Expected output:
# deployment.apps/api-deployment rolled back

Expected behavior: The rolling update replaces Pods one at a time (maxUnavailable: 1). If the new Pod fails its readiness probe, the update pauses. Rolling back reverts to the previous ReplicaSet.

Services — Stable Networking

Pods are ephemeral — they get new IP addresses when they restart. A Service provides a stable IP and DNS name that load-balances traffic across the Pods matching its selector.

# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: api-service
spec:
  selector:
    app: api
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
  type: ClusterIP  # Default: internal-only
# LoadBalancer service for external access
apiVersion: v1
kind: Service
metadata:
  name: web-service
spec:
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 80
  type: LoadBalancer  # Cloud providers provision an external LB

Expected behavior: ClusterIP services are accessible only within the cluster at api-service:80. LoadBalancer services expose the app externally through a cloud load balancer. Both distribute traffic across healthy Pods automatically.

# Expose the Deployment as a Service
kubectl expose deployment api-deployment \
  --name=api-service \
  --port=80 \
  --target-port=8080

# Expected output:
# service/api-service exposed

# Get Service details
kubectl get svc api-service

# Expected output:
# NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
# api-service   ClusterIP   10.96.123.45    <none>        80/TCP    10s

# Test internal access
kubectl run test-pod --image=busybox -it --rm -- \
  wget -qO- http://api-service

# Expected output:
# <!DOCTYPE html>... (application response)

ConfigMaps and Secrets

Configuration and sensitive data should not be hardcoded in manifests. ConfigMaps store non-sensitive configuration, while Secrets store sensitive data.

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  NODE_ENV: production
  LOG_LEVEL: info
  API_URL: https://api.example.com
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
stringData:
  DB_USER: admin
  DB_PASSWORD: supersecret  # Base64-encoded in practice
# deployment-with-config.yaml
spec:
  containers:
    - name: api
      envFrom:
        - configMapRef:
            name: app-config
        - secretRef:
            name: db-credentials

Expected behavior: Environment variables from ConfigMaps and Secrets are injected into the container at runtime. Secrets are stored base64-encoded and should be encrypted at rest using Kubernetes encryption providers.

Horizontal Pod Autoscaling

Kubernetes can automatically adjust the number of Pod replicas based on CPU, memory, or custom metrics.

# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api-autoscaler
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api-deployment
  minReplicas: 3
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70

Expected behavior: When CPU utilization across all Pods exceeds 70%, the HPA increases replicas up to 20. When utilization drops, it scales down to a minimum of 3.

Common Errors

  1. Not setting resource requests and limits: Without resource requests, the scheduler places too many Pods on a single node, causing resource starvation. Without limits, a single Pod can consume all node memory.

  2. Missing readiness probes: Without readiness probes, the Service routes traffic to Pods before the application is ready to handle requests, causing 502 errors during deployments and scaling events.

  3. Using latest image tag: imagePullPolicy defaults to Always for latest tags, causing unnecessary pulls and making rollbacks impossible because the previous version is unknown.

  4. Overlooking Pod anti-affinity: Multiple replicas scheduled on the same node create a single point of failure. That node goes down and all replicas go with it.

  5. Forgetting to set terminationGracePeriodSeconds: When Kubernetes terminates a Pod, it sends SIGTERM then waits. If your app needs more than 30 seconds to drain connections, set this explicitly.

Practice Questions

  1. What is the difference between a Pod and a Deployment? Answer: A Pod is a single instance of one or more containers. A Deployment manages a ReplicaSet that ensures the desired number of Pods are always running, and supports rolling updates and rollbacks.

  2. How does a Service select which Pods to route traffic to? Answer: The Service uses label selectors. Any Pod with labels matching the Service's spec.selector receives traffic. This is how Kubernetes achieves loose coupling between networking and workloads.

  3. What is the difference between liveness and readiness probes? Answer: Liveness probes determine if the app is alive — if they fail, the kubelet restarts the container. Readiness probes determine if the app can serve traffic — if they fail, the Service stops sending traffic to that Pod.

  4. How does a rolling update ensure zero downtime? Answer: The maxUnavailable and maxSurge parameters control how many old Pods are terminated and new Pods are created simultaneously. At most maxUnavailable Pods are down at any time, ensuring enough replicas remain to serve traffic.

Challenge

Deploy a two-tier application: create a Deployment for a Node.js API with 3 replicas, resource requests/limits, liveness and readiness probes, and a rolling update Strategy. Expose it internally with a ClusterIP Service. Add a ConfigMap for environment configuration and a Secret for database credentials. Configure an HPA to scale between 3 and 10 replicas at 70% CPU. Perform a rolling update from v1 to v2, then roll back.

Mini Project

Write a complete set of Kubernetes manifests for a web application: a Deployment for the frontend with nginx, a Deployment for the API with health probes and rolling update Strategy, a Service for each, a ConfigMap with application configuration, a Secret for database credentials, and an Ingress to route external traffic. Deploy to a local cluster with minikube or kind, perform a rolling update, verify zero-downtime behavior by monitoring requests during the update, and practice rolling back a failed release.

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro