Kubernetes Complete Guide: Pods, Deployments, Services & kubectl

Published February 12, 2026 · 35 min read

Kubernetes (K8s) is the industry standard for container orchestration. While Docker Compose handles single-server deployments well, Kubernetes manages containers across clusters of machines with automatic scaling, self-healing, rolling deployments, and service discovery. It is the infrastructure layer behind most production workloads at scale.

This guide covers Kubernetes from architecture fundamentals through production-ready configurations. You will learn every core concept, write real manifests, and understand the kubectl commands you will use daily. If you are new to containers, start with our Docker Compose guide first.

⚙ Related: Generate K8s manifests with the Kubernetes YAML Generator and validate YAML syntax with our YAML Validator.

Table of Contents

  1. What is Kubernetes and Why Use It
  2. Architecture: Control Plane and Worker Nodes
  3. Core Objects: Pods, ReplicaSets, Deployments, Services
  4. kubectl Basics
  5. Pod Lifecycle and Health Checks
  6. Deployments: Rolling Updates, Rollbacks, Scaling
  7. Services: ClusterIP, NodePort, LoadBalancer
  8. ConfigMaps and Secrets
  9. Persistent Volumes and Claims
  10. Ingress Controllers and Rules
  11. Resource Limits and Autoscaling
  12. Helm Charts Basics
  13. Debugging and Troubleshooting
  14. Best Practices for Production
  15. kubectl Commands Cheat Sheet
  16. Frequently Asked Questions

1. What is Kubernetes and Why Use It

Kubernetes is an open-source container orchestration platform originally developed by Google and now maintained by the Cloud Native Computing Foundation (CNCF). It automates the deployment, scaling, and management of containerized applications across clusters of machines.

Why Kubernetes matters:

2. Architecture: Control Plane and Worker Nodes

A Kubernetes cluster has two types of machines: the control plane (master) that makes scheduling decisions, and worker nodes that run your application containers.

Control Plane Components

Worker Node Components

# View cluster components and their status
kubectl get componentstatuses
kubectl get nodes -o wide
kubectl cluster-info

3. Core Objects: Pods, ReplicaSets, Deployments, Services

Pods

A Pod is the smallest deployable unit — one or more containers that share the same network namespace (they can reach each other on localhost) and storage volumes.

# pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    app: nginx
spec:
  containers:
    - name: nginx
      image: nginx:1.25-alpine
      ports:
        - containerPort: 80
      resources:
        requests:
          memory: "64Mi"
          cpu: "100m"
        limits:
          memory: "128Mi"
          cpu: "250m"

ReplicaSets

A ReplicaSet ensures that a specified number of Pod replicas are running at all times. You rarely create ReplicaSets directly — Deployments manage them for you.

Deployments

Deployments are the primary way to run stateless applications. They manage ReplicaSets and provide rolling updates and rollbacks.

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  labels:
    app: web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
        - name: web
          image: myapp:v1.2.0
          ports:
            - containerPort: 3000
          resources:
            requests:
              memory: "128Mi"
              cpu: "100m"
            limits:
              memory: "256Mi"
              cpu: "500m"

Services

Services provide stable networking for Pods. Since Pods are ephemeral (they come and go), a Service gives you a permanent IP address and DNS name that routes to the current set of healthy Pods matching a label selector.

Namespaces

Namespaces partition a cluster into virtual sub-clusters. They are useful for isolating teams, environments (dev/staging/prod), or applications.

# Create and use namespaces
kubectl create namespace staging
kubectl get pods --namespace staging
kubectl config set-context --current --namespace=staging

4. kubectl Basics

kubectl is the command-line tool for interacting with Kubernetes clusters. Every operation follows the pattern: kubectl [verb] [resource] [name] [flags].

# Get resources (list)
kubectl get pods                        # list pods in current namespace
kubectl get pods -A                     # list pods in ALL namespaces
kubectl get pods -o wide                # show node, IP, and more details
kubectl get deployments                 # list deployments
kubectl get services                    # list services
kubectl get all                         # list common resources

# Describe (detailed info + events)
kubectl describe pod nginx-pod          # full details and recent events
kubectl describe deployment web-app     # deployment status and conditions
kubectl describe node worker-1          # node capacity and allocations

# Create and apply
kubectl create -f pod.yaml              # create from file (fails if exists)
kubectl apply -f deployment.yaml        # create or update from file
kubectl apply -f ./k8s/                 # apply all files in a directory

# Delete
kubectl delete pod nginx-pod            # delete a specific pod
kubectl delete -f deployment.yaml       # delete resources defined in file
kubectl delete deployment web-app       # delete by resource type and name

# Logs
kubectl logs web-app-7d9f8b6c5-x2k4m   # view pod logs
kubectl logs -f web-app-7d9f8b6c5-x2k4m # follow (tail) logs
kubectl logs web-app-7d9f8b6c5-x2k4m -c sidecar  # specific container
kubectl logs -l app=web-app --tail=100  # logs from all pods with label

# Exec into containers
kubectl exec -it web-app-7d9f8b6c5-x2k4m -- /bin/sh   # interactive shell
kubectl exec web-app-7d9f8b6c5-x2k4m -- env            # run a command

5. Pod Lifecycle and Health Checks

Pods move through phases: Pending (waiting to be scheduled), Running (at least one container is running), Succeeded (all containers exited successfully), Failed (a container exited with an error), and Unknown (node communication lost).

Liveness Probes

Liveness probes detect if a container is stuck or deadlocked. If the probe fails, Kubernetes restarts the container.

Readiness Probes

Readiness probes determine if a Pod is ready to accept traffic. Failing Pods are removed from Service endpoints but not restarted.

Startup Probes

Startup probes run only during initialization. They disable liveness and readiness checks until the application has fully started, preventing premature restarts for slow-starting apps.

# All three probes in a Pod spec
apiVersion: v1
kind: Pod
metadata:
  name: app-with-probes
spec:
  containers:
    - name: app
      image: myapp:v1.0
      ports:
        - containerPort: 8080
      startupProbe:
        httpGet:
          path: /healthz
          port: 8080
        failureThreshold: 30    # 30 x 10s = 5 min max startup time
        periodSeconds: 10
      livenessProbe:
        httpGet:
          path: /healthz
          port: 8080
        initialDelaySeconds: 0  # startup probe handles the delay
        periodSeconds: 15
        failureThreshold: 3
      readinessProbe:
        httpGet:
          path: /ready
          port: 8080
        periodSeconds: 5
        failureThreshold: 3

Probes support four mechanisms: httpGet (HTTP request), tcpSocket (TCP connection), exec (run a command), and grpc (gRPC health check).

# TCP socket probe (for databases, non-HTTP services)
livenessProbe:
  tcpSocket:
    port: 5432
  periodSeconds: 10

# Exec probe (run a command inside the container)
livenessProbe:
  exec:
    command: ["pg_isready", "-U", "postgres"]
  periodSeconds: 10

# gRPC probe (for gRPC services)
livenessProbe:
  grpc:
    port: 50051
  periodSeconds: 10

6. Deployments: Rolling Updates, Rollbacks, Scaling

Rolling Updates

When you update a Deployment's Pod template (e.g., change the image tag), Kubernetes performs a rolling update — gradually replacing old Pods with new ones so the application stays available.

# Update the image (triggers rolling update)
kubectl set image deployment/web-app web=myapp:v1.3.0

# Or edit the YAML and apply
kubectl apply -f deployment.yaml

# Watch the rollout progress
kubectl rollout status deployment/web-app

# Control the rollout strategy
# In your deployment.yaml spec:
#   strategy:
#     type: RollingUpdate
#     rollingUpdate:
#       maxSurge: 1          # max pods above desired count during update
#       maxUnavailable: 0    # zero downtime: never reduce below desired

Rollbacks

# View rollout history
kubectl rollout history deployment/web-app

# Roll back to previous version
kubectl rollout undo deployment/web-app

# Roll back to a specific revision
kubectl rollout undo deployment/web-app --to-revision=3

Scaling

# Manual scaling
kubectl scale deployment/web-app --replicas=5

# Scale to zero (stop all pods)
kubectl scale deployment/web-app --replicas=0

7. Services: ClusterIP, NodePort, LoadBalancer

ClusterIP (default)

Exposes the Service on an internal cluster IP. Only reachable from within the cluster. Use this for internal communication between services.

apiVersion: v1
kind: Service
metadata:
  name: api-service
spec:
  type: ClusterIP           # default, can be omitted
  selector:
    app: api
  ports:
    - port: 80              # service port (what other pods connect to)
      targetPort: 3000      # container port (where your app listens)

NodePort

Exposes the Service on a static port (30000-32767) on every node's IP. Useful for development or when you do not have a cloud load balancer.

apiVersion: v1
kind: Service
metadata:
  name: web-nodeport
spec:
  type: NodePort
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080
      nodePort: 30080       # accessible at node-ip:30080

LoadBalancer

Provisions a cloud load balancer (on AWS, GCP, Azure) that routes external traffic to the Service. The most common way to expose applications to the internet in cloud environments.

apiVersion: v1
kind: Service
metadata:
  name: web-lb
spec:
  type: LoadBalancer
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080

ExternalName

Maps the Service to an external DNS name. No proxying occurs — it returns a CNAME record. Useful for accessing external databases or APIs by a consistent in-cluster name.

apiVersion: v1
kind: Service
metadata:
  name: external-db
spec:
  type: ExternalName
  externalName: db.example.com

8. ConfigMaps and Secrets

ConfigMaps

ConfigMaps store non-sensitive configuration data as key-value pairs. They decouple configuration from container images.

# Create from literal values
kubectl create configmap app-config \
  --from-literal=DATABASE_HOST=db-service \
  --from-literal=LOG_LEVEL=info

# Create from a file
kubectl create configmap nginx-conf --from-file=nginx.conf

# ConfigMap YAML
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  DATABASE_HOST: "db-service"
  LOG_LEVEL: "info"
  config.json: |
    {
      "port": 3000,
      "features": { "cache": true }
    }

Using ConfigMaps in Pods

# In your Pod spec:
spec:
  containers:
    - name: app
      image: myapp:v1.0
      # As environment variables
      envFrom:
        - configMapRef:
            name: app-config
      # Or mount as files
      volumeMounts:
        - name: config-vol
          mountPath: /etc/config
  volumes:
    - name: config-vol
      configMap:
        name: app-config

Secrets

Secrets store sensitive data (passwords, tokens, keys). They are base64-encoded, not encrypted by default — enable encryption at rest for production clusters.

# Create a secret
kubectl create secret generic db-credentials \
  --from-literal=username=admin \
  --from-literal=password=s3cretP@ss

# Use stringData in YAML for plain-text (K8s encodes it for you)
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
stringData:
  username: admin
  password: s3cretP@ss
# Inject secrets into a Pod as environment variables
spec:
  containers:
    - name: app
      image: myapp:v1.0
      env:
        - name: DB_USERNAME
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: username
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: password

9. Persistent Volumes and Claims

Pods are ephemeral — their filesystem is lost when they restart. Persistent Volumes (PV) provide cluster-wide storage, and Persistent Volume Claims (PVC) let Pods request storage without knowing the underlying infrastructure.

# PersistentVolumeClaim (what your Pod requests)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-data
spec:
  accessModes:
    - ReadWriteOnce          # single node read-write
  resources:
    requests:
      storage: 10Gi
  storageClassName: standard # cloud provider default storage class
# Use the PVC in a Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:16-alpine
          env:
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: password
          volumeMounts:
            - name: data
              mountPath: /var/lib/postgresql/data
          ports:
            - containerPort: 5432
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: postgres-data

Access modes: ReadWriteOnce (single node), ReadOnlyMany (multiple nodes read-only), ReadWriteMany (multiple nodes read-write, requires NFS or similar).

10. Ingress Controllers and Rules

Ingress provides HTTP/HTTPS routing from outside the cluster to Services inside. Unlike LoadBalancer Services (one per service), a single Ingress controller handles routing for many services based on hostnames and paths.

# Install an ingress controller (nginx-ingress via Helm)
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx

# Ingress resource with TLS
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - app.example.com
      secretName: app-tls
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: frontend
                port:
                  number: 80
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: api-service
                port:
                  number: 80

The Ingress controller (nginx, Traefik, HAProxy, or cloud-native like AWS ALB) must be installed separately. Ingress resources are just routing rules — they need a controller to implement them.

11. Resource Limits and Autoscaling

Resource Requests and Limits

Requests guarantee minimum resources for scheduling. Limits cap maximum usage. Set both for every container in production.

spec:
  containers:
    - name: app
      image: myapp:v1.0
      resources:
        requests:
          memory: "128Mi"    # guaranteed minimum (used for scheduling)
          cpu: "100m"        # 100 millicores = 0.1 CPU core
        limits:
          memory: "256Mi"    # OOMKilled if exceeded
          cpu: "500m"        # throttled if exceeded

CPU is measured in millicores (1000m = 1 CPU core). Memory uses standard units (Mi, Gi). If a container exceeds its memory limit, it is OOMKilled. If it exceeds CPU limit, it is throttled but not killed.

Horizontal Pod Autoscaler (HPA)

HPA automatically scales the number of Pod replicas based on observed metrics.

# Create HPA via kubectl
kubectl autoscale deployment web-app \
  --min=2 --max=10 --cpu-percent=70

# HPA YAML manifest
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80

# Check HPA status
kubectl get hpa

HPA requires the Metrics Server to be installed in the cluster. Most managed Kubernetes services (EKS, GKE, AKS) include it by default.

12. Helm Charts Basics

Helm is the package manager for Kubernetes. It bundles manifests into reusable, versioned charts with configurable values.

# Add a chart repository
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

# Search for charts
helm search repo postgresql

# Install a chart
helm install my-postgres bitnami/postgresql \
  --set auth.postgresPassword=mypassword \
  --set primary.persistence.size=20Gi \
  --namespace databases --create-namespace

# List installed releases
helm list -A

# Upgrade a release
helm upgrade my-postgres bitnami/postgresql \
  --set auth.postgresPassword=mypassword \
  --set primary.persistence.size=50Gi

# Rollback to previous version
helm rollback my-postgres 1

# Uninstall
helm uninstall my-postgres -n databases

Chart Structure

my-chart/
  Chart.yaml          # chart metadata (name, version, dependencies)
  values.yaml         # default configuration values
  templates/          # Kubernetes manifest templates
    deployment.yaml
    service.yaml
    ingress.yaml
    _helpers.tpl      # template helper functions
  charts/             # dependency charts

Values in values.yaml are injected into templates using Go template syntax. Override defaults with --set key=value or -f custom-values.yaml at install/upgrade time.

13. Debugging and Troubleshooting

Pod Not Starting

# Check pod status and events
kubectl describe pod <pod-name>

# Common issues in Events section:
# - ImagePullBackOff: wrong image name/tag or missing registry auth
# - CrashLoopBackOff: container starts and crashes repeatedly
# - Pending: insufficient resources or no matching node

# Check container logs (even for crashed pods)
kubectl logs <pod-name> --previous

Service Not Reachable

# Verify endpoints exist (Pods match the selector)
kubectl get endpoints <service-name>

# If endpoints are empty, labels do not match
kubectl get pods --show-labels
kubectl describe service <service-name>

# Test connectivity from inside the cluster
kubectl run debug --rm -it --image=busybox -- /bin/sh
# Inside the debug pod: wget -qO- http://service-name:port

Port Forwarding for Local Debugging

# Forward a pod port to localhost
kubectl port-forward pod/web-app-xyz 8080:3000

# Forward a service port
kubectl port-forward svc/api-service 8080:80

# Now access http://localhost:8080 in your browser

Events and Cluster State

# View recent cluster events (sorted by time)
kubectl get events --sort-by=.metadata.creationTimestamp

# View events for a specific namespace
kubectl get events -n staging

# Check node conditions and resource pressure
kubectl describe node <node-name>
⚙ Related: Use our Docker Commands Cheat Sheet alongside kubectl for container-level debugging.

14. Best Practices for Production

# Pod Disruption Budget example
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: web-app-pdb
spec:
  minAvailable: 2           # or use maxUnavailable: 1
  selector:
    matchLabels:
      app: web-app

15. kubectl Commands Cheat Sheet

# -- CLUSTER INFO --
kubectl cluster-info                    # cluster endpoint info
kubectl get nodes -o wide               # list nodes with IPs
kubectl top nodes                       # node resource usage
kubectl top pods                        # pod resource usage

# -- PODS --
kubectl get pods                        # list pods
kubectl get pods -A                     # all namespaces
kubectl get pods -o wide                # show IPs and nodes
kubectl describe pod <name>             # detailed info + events
kubectl logs <pod>                      # view logs
kubectl logs -f <pod>                   # follow logs
kubectl logs <pod> -c <container>       # multi-container pod
kubectl logs -l app=web --tail=50       # by label selector
kubectl exec -it <pod> -- /bin/sh       # shell into pod
kubectl delete pod <name>               # delete pod
kubectl port-forward <pod> 8080:3000    # local port forward

# -- DEPLOYMENTS --
kubectl get deployments                 # list deployments
kubectl apply -f deployment.yaml        # create or update
kubectl set image deploy/name app=img:v2  # update image
kubectl scale deploy/name --replicas=5  # scale replicas
kubectl rollout status deploy/name      # watch rollout
kubectl rollout history deploy/name     # revision history
kubectl rollout undo deploy/name        # rollback
kubectl rollout restart deploy/name     # restart all pods

# -- SERVICES --
kubectl get svc                         # list services
kubectl describe svc <name>             # endpoints and details
kubectl get endpoints <name>            # pod IPs behind service
kubectl port-forward svc/name 8080:80   # forward service port

# -- CONFIG AND SECRETS --
kubectl get configmaps                  # list configmaps
kubectl get secrets                     # list secrets
kubectl create configmap name --from-literal=KEY=val
kubectl create secret generic name --from-literal=KEY=val

# -- NAMESPACES --
kubectl get namespaces                  # list namespaces
kubectl create namespace name           # create namespace
kubectl config set-context --current --namespace=name

# -- DEBUGGING --
kubectl get events --sort-by=.metadata.creationTimestamp
kubectl run debug --rm -it --image=busybox -- sh
kubectl auth can-i create deployments   # check permissions
kubectl explain pod.spec.containers     # built-in API docs

Frequently Asked Questions

What is the difference between a Pod and a Deployment?

A Pod is the smallest deployable unit in Kubernetes, representing one or more containers that share networking and storage. A Deployment is a higher-level controller that manages Pods through ReplicaSets. While you can create Pods directly, Deployments provide declarative updates, rolling rollouts, rollbacks, and self-healing by automatically replacing failed Pods. In production you should almost always use Deployments rather than creating Pods directly.

What are the different types of Kubernetes Services?

Kubernetes has four Service types. ClusterIP (the default) exposes the Service on an internal IP reachable only within the cluster. NodePort exposes the Service on a static port on every node's IP, allowing external access. LoadBalancer provisions a cloud load balancer that routes external traffic to the Service. ExternalName maps the Service to an external DNS name without proxying, useful for accessing external resources by a consistent in-cluster name.

How do I pass configuration and secrets to Pods?

Kubernetes provides ConfigMaps for non-sensitive configuration and Secrets for sensitive data like passwords and API keys. Both can be injected into Pods as environment variables or mounted as files. ConfigMaps store data in plain text, while Secrets are base64-encoded (not encrypted by default, but can be encrypted at rest with EncryptionConfiguration). For production, use external secret managers like HashiCorp Vault, AWS Secrets Manager, or the Kubernetes External Secrets Operator.

What is the difference between liveness, readiness, and startup probes?

Liveness probes check if a container is running correctly; if a liveness probe fails, Kubernetes restarts the container. Readiness probes check if a container is ready to accept traffic; if it fails, the Pod is removed from Service endpoints but not restarted. Startup probes run only during container initialization and disable liveness and readiness checks until they succeed, which is useful for slow-starting applications. All three probes support HTTP GET, TCP socket, exec command, and gRPC check methods.

When should I use Kubernetes instead of Docker Compose?

Use Docker Compose for single-server applications, local development environments, and simple deployments where you do not need multi-node orchestration. Use Kubernetes when you need horizontal auto-scaling, self-healing across multiple nodes, rolling deployments with zero downtime, service discovery across a cluster, or when running in a cloud environment that provides managed Kubernetes (EKS, GKE, AKS). Kubernetes adds significant operational complexity, so it should be justified by actual scaling or availability requirements.

What is Helm and why do I need it?

Helm is the package manager for Kubernetes. It bundles multiple Kubernetes manifests (Deployments, Services, ConfigMaps, etc.) into reusable charts with templated values. Instead of managing dozens of YAML files, you install an application with a single command like helm install my-release chart-name. Helm handles versioning, upgrades, rollbacks, and dependency management. It is especially valuable for deploying complex applications like databases, monitoring stacks, or ingress controllers that require many coordinated Kubernetes resources.

Conclusion

Kubernetes is a powerful but complex system. Start with the fundamentals — Pods, Deployments, Services, and kubectl — and add complexity incrementally: health probes, ConfigMaps, Ingress, autoscaling, then Helm. Do not adopt Kubernetes until your application genuinely needs multi-node orchestration, automatic scaling, or zero-downtime rolling deployments. For single-server workloads, Docker Compose is simpler and equally effective.

When you do adopt Kubernetes, invest in proper resource limits, health checks on every container, GitOps workflows, and monitoring from day one. These practices prevent the majority of production incidents.

⚙ Related: Automate your deployments with GitHub Actions CI/CD, manage infrastructure as code with Terraform, and master container networking with the Docker Networking Guide.

Learn More

Related Resources

Docker Compose: The Complete Guide
Services, networks, volumes, and production deployment
Docker Networking Guide
Bridge, overlay, and host networking for containers
GitHub Actions CI/CD Guide
Automate builds, tests, and Kubernetes deployments
Terraform Complete Guide
Infrastructure as code for Kubernetes clusters and cloud resources