Kubernetes Tutorial for Beginners: Complete Guide (2026)

Learn Kubernetes from scratch. This guide walks you through every core concept — pods, deployments, services, ConfigMaps, secrets, scaling, debugging, and Helm — with hands-on YAML examples you can copy and run.

What Is Kubernetes?

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

Where Docker builds and runs individual containers, Kubernetes answers the harder questions: How do you run 50 copies of your application across 10 servers? How do you roll out updates without downtime? What happens when a server goes down? How do you route traffic to healthy instances?

Core Architecture

A Kubernetes cluster consists of a control plane (the brain) and worker nodes (the machines that run your containers). Here is what each component does:

Component Location Role
kube-apiserver Control Plane Front door for all API requests. kubectl talks to this.
etcd Control Plane Key-value store holding all cluster state.
kube-scheduler Control Plane Decides which node runs each new Pod.
controller-manager Control Plane Ensures desired state matches actual state (runs controllers).
kubelet Worker Node Agent that runs Pods on each node.
kube-proxy Worker Node Handles network routing and load balancing.

You interact with Kubernetes through kubectl, the command-line tool that sends requests to the API server. Everything in Kubernetes is declared as YAML manifests: you describe the desired state, and Kubernetes works to make it real.

Setting Up Your Environment

You need two things to follow this tutorial: kubectl (the CLI) and a local cluster. The fastest way to get a local cluster running in 2026:

Option 1: Docker Desktop (Simplest)

bash — Enable Kubernetes in Docker Desktop
# Docker Desktop includes a built-in Kubernetes cluster
# Settings > Kubernetes > Enable Kubernetes > Apply & Restart

# Verify it is running
kubectl cluster-info
kubectl get nodes

Option 2: Minikube (Most Popular)

bash — Minikube setup
# Install minikube (macOS)
brew install minikube

# Start a cluster
minikube start

# Verify
kubectl get nodes
# NAME       STATUS   ROLES           AGE   VERSION
# minikube   Ready    control-plane   1m    v1.31.0

# Enable useful addons
minikube addons enable ingress
minikube addons enable metrics-server
minikube addons enable dashboard

# Open the dashboard
minikube dashboard

Option 3: kind (Kubernetes in Docker)

bash — kind setup
# Install kind
brew install kind

# Create a cluster
kind create cluster --name dev

# Verify
kubectl cluster-info --context kind-dev
Which Should You Choose?

Docker Desktop for the simplest setup. Minikube for the most features (add-ons, multi-node). kind for CI/CD pipelines and when you need multiple clusters. All three are free and run locally.

Pods: The Smallest Unit

A Pod is the smallest deployable unit in Kubernetes. It wraps one or more containers that share the same network namespace (they communicate via localhost) and storage volumes. In practice, most Pods run a single container.

Creating Your First Pod

pod.yaml — Minimal Pod definition
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    app: nginx
spec:
  containers:
    - name: nginx
      image: nginx:1.27-alpine
      ports:
        - containerPort: 80
      resources:
        requests:
          memory: "64Mi"
          cpu: "100m"
        limits:
          memory: "128Mi"
          cpu: "250m"
bash — Pod commands
# Create the Pod
kubectl apply -f pod.yaml

# Check Pod status
kubectl get pods
# NAME        READY   STATUS    RESTARTS   AGE
# nginx-pod   1/1     Running   0          15s

# Detailed Pod information
kubectl describe pod nginx-pod

# View Pod logs
kubectl logs nginx-pod

# Follow logs in real time
kubectl logs -f nginx-pod

# Open a shell inside the Pod
kubectl exec -it nginx-pod -- /bin/sh

# Port-forward to access from localhost
kubectl port-forward nginx-pod 8080:80
# Visit http://localhost:8080

# Delete the Pod
kubectl delete pod nginx-pod
Do Not Deploy Bare Pods in Production

Bare Pods are not rescheduled if the node fails. Always use a Deployment (or StatefulSet, DaemonSet, Job) to manage Pods. These controllers ensure your Pods are recreated if they crash or a node goes down.

Deployments: Managing Replicas

A Deployment is the standard way to run stateless applications. It manages a set of identical Pods (called replicas), handles rolling updates, and automatically replaces Pods that crash or get evicted. You describe the desired state, and the Deployment controller makes it happen.

deployment.yaml — Web application Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  labels:
    app: web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-app
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
        - name: web
          image: myregistry.com/web-app:v1.0.0
          ports:
            - containerPort: 3000
          env:
            - name: NODE_ENV
              value: "production"
          resources:
            requests:
              memory: "128Mi"
              cpu: "100m"
            limits:
              memory: "256Mi"
              cpu: "500m"
          readinessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 15
            periodSeconds: 20
bash — Deployment commands
# Apply the Deployment
kubectl apply -f deployment.yaml

# Check rollout status
kubectl rollout status deployment/web-app

# List Deployments
kubectl get deployments

# See the Pods created by the Deployment
kubectl get pods -l app=web-app

# Scale to 5 replicas
kubectl scale deployment web-app --replicas=5

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

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

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

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

# Rollback to a specific revision
kubectl rollout undo deployment/web-app --to-revision=2

The RollingUpdate strategy with maxUnavailable: 0 ensures zero downtime during deployments: Kubernetes creates new Pods before removing old ones. The readiness probe controls when a Pod receives traffic, preventing requests from reaching containers that are not yet ready.

Services: Networking and Discovery

Pods are ephemeral. They get new IP addresses every time they restart. A Service provides a stable network identity and load balances traffic across a set of Pods. Services use label selectors to find their target Pods.

Service Types

Type Access Scope Use Case
ClusterIP Internal only Service-to-service communication within the cluster
NodePort External via node IP + port Development, testing
LoadBalancer External via cloud LB Production external access (AWS ALB, GCP LB)
ExternalName DNS alias Point to external services (e.g., RDS database)
service.yaml — ClusterIP and LoadBalancer
# Internal service (ClusterIP - default)
apiVersion: v1
kind: Service
metadata:
  name: web-app-internal
spec:
  selector:
    app: web-app
  ports:
    - port: 80
      targetPort: 3000
      protocol: TCP
  type: ClusterIP
---
# External service (LoadBalancer)
apiVersion: v1
kind: Service
metadata:
  name: web-app-public
spec:
  selector:
    app: web-app
  ports:
    - port: 80
      targetPort: 3000
      protocol: TCP
  type: LoadBalancer

Ingress: HTTP Routing

An Ingress resource provides HTTP/HTTPS routing with path-based and host-based rules. Instead of one LoadBalancer per service (expensive), a single Ingress controller routes traffic to many services.

ingress.yaml — Host and path-based routing
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - app.example.com
        - api.example.com
      secretName: app-tls
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web-app-internal
                port:
                  number: 80
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: api-service
                port:
                  number: 80

When generating Nginx configurations for your Ingress controller, the Nginx Config Generator helps you create custom server blocks with SSL, rate limiting, and caching directives.

ConfigMaps and Secrets

ConfigMaps store non-sensitive configuration data. Secrets store sensitive data like passwords, API keys, and TLS certificates. Both decouple configuration from container images so you can use the same image across development, staging, and production.

ConfigMaps

configmap.yaml — Application configuration
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  APP_ENV: "production"
  LOG_LEVEL: "info"
  MAX_CONNECTIONS: "100"
  config.json: |
    {
      "features": {
        "darkMode": true,
        "betaAccess": false
      },
      "pagination": {
        "defaultLimit": 25,
        "maxLimit": 100
      }
    }
deployment-with-config.yaml — Using ConfigMap in a Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
        - name: web
          image: myregistry.com/web-app:v1.0.0
          # Inject as environment variables
          envFrom:
            - configMapRef:
                name: app-config
          # Or mount as files
          volumeMounts:
            - name: config-volume
              mountPath: /app/config
              readOnly: true
      volumes:
        - name: config-volume
          configMap:
            name: app-config
            items:
              - key: config.json
                path: config.json

Secrets

bash — Creating Secrets
# Create from literal values
kubectl create secret generic db-credentials \
  --from-literal=username=admin \
  --from-literal=password='s3cur3-p@ssw0rd'

# Create from a file
kubectl create secret generic tls-cert \
  --from-file=cert.pem=./certs/server.crt \
  --from-file=key.pem=./certs/server.key

# Create from .env file
kubectl create secret generic app-secrets \
  --from-env-file=.env.production

# View secret (base64 encoded)
kubectl get secret db-credentials -o yaml

# Decode a specific value
kubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 --decode

When you need to encode or decode base64 values for Kubernetes secrets, the Base64 Encoder/Decoder handles encoding in both directions without installing any tools.

Secrets Are Not Encrypted by Default

Kubernetes Secrets are base64-encoded, not encrypted. Anyone with kubectl access can decode them. In production, enable encryption at rest for etcd, use a secrets manager (Vault, AWS Secrets Manager, GCP Secret Manager), and restrict access with RBAC.

Scaling: Manual and Automatic

Kubernetes supports three types of scaling: horizontal (more Pods), vertical (bigger Pods), and cluster-level (more nodes).

Manual Scaling

bash — Manual scaling
# Scale to 5 replicas
kubectl scale deployment web-app --replicas=5

# Scale to 0 (useful for cost savings on non-prod)
kubectl scale deployment web-app --replicas=0

# Verify
kubectl get deployment web-app
# NAME      READY   UP-TO-DATE   AVAILABLE   AGE
# web-app   5/5     5            5           10m

Horizontal Pod Autoscaler (HPA)

hpa.yaml — Autoscale based on CPU
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
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Pods
          value: 1
          periodSeconds: 60
bash — HPA commands
# Apply the HPA
kubectl apply -f hpa.yaml

# Watch HPA in action
kubectl get hpa --watch
# NAME          REFERENCE            TARGETS    MINPODS   MAXPODS   REPLICAS
# web-app-hpa   Deployment/web-app   45%/70%    2         10        3

# Quick HPA creation from CLI
kubectl autoscale deployment web-app \
  --min=2 --max=10 --cpu-percent=70

The stabilizationWindowSeconds prevents flapping: the HPA waits 5 minutes before scaling down, so brief traffic dips do not cause unnecessary churn.

Persistent Storage

Pods are ephemeral, but many applications need data that survives Pod restarts. PersistentVolumes (PV) and PersistentVolumeClaims (PVC) provide durable storage.

pvc.yaml — PersistentVolumeClaim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-data
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: standard
  resources:
    requests:
      storage: 10Gi
---
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
          ports:
            - containerPort: 5432
          env:
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: password
          volumeMounts:
            - name: data
              mountPath: /var/lib/postgresql/data
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: postgres-data
StatefulSet vs Deployment for Databases

Use a StatefulSet instead of a Deployment for databases and other stateful applications. StatefulSets provide stable network identities (postgres-0, postgres-1), ordered deployment and scaling, and stable persistent storage per replica.

Debugging and Troubleshooting

When things break in Kubernetes, systematic debugging is essential. Here are the commands and techniques that solve 90% of issues.

Essential kubectl Debug Commands

bash — Debugging toolkit
# Check Pod status and events
kubectl get pods -o wide
kubectl describe pod <pod-name>

# View logs (current container)
kubectl logs <pod-name>

# View logs from previous crashed container
kubectl logs <pod-name> --previous

# View logs from a specific container in a multi-container Pod
kubectl logs <pod-name> -c <container-name>

# Follow logs in real time
kubectl logs -f <pod-name>

# Stream logs from all Pods with a label
kubectl logs -l app=web-app --all-containers -f

# Open a shell in a running Pod
kubectl exec -it <pod-name> -- /bin/sh

# Run a temporary debug Pod on the cluster
kubectl run debug --rm -it --image=busybox -- /bin/sh

# Check resource usage (requires metrics-server)
kubectl top pods
kubectl top nodes

# View cluster events (sorted by time)
kubectl get events --sort-by='.lastTimestamp'

# Check node conditions
kubectl describe node <node-name>

# View all resources in a namespace
kubectl get all -n <namespace>

Common Pod Status Problems

Status Meaning First Debug Step
Pending Cannot be scheduled kubectl describe pod — check Events for resource/node issues
CrashLoopBackOff Container keeps crashing kubectl logs --previous — see crash output
ImagePullBackOff Cannot pull container image Check image name, tag, registry credentials
OOMKilled Out of memory Increase memory limits in Pod spec
CreateContainerError Container config error kubectl describe pod — check ConfigMap/Secret refs

When you need to inspect the JSON output from kubectl get -o json or kubectl describe, the JSON Formatter makes complex Kubernetes resource definitions readable.

Helm: Package Management

Helm is the package manager for Kubernetes. It bundles related manifests into charts — reusable, version-controlled packages that can be configured with different values for each environment.

Installing Helm

bash — Helm setup
# Install Helm (macOS)
brew install helm

# Add the official charts repository
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

# Search for a chart
helm search repo postgresql

Using Helm Charts

bash — Installing and managing charts
# Install PostgreSQL with custom values
helm install my-postgres bitnami/postgresql \
  --set auth.postgresPassword=mysecretpass \
  --set primary.persistence.size=20Gi \
  --namespace databases --create-namespace

# Install with a values file
helm install my-postgres bitnami/postgresql \
  -f values-production.yaml \
  --namespace databases

# List installed releases
helm list --all-namespaces

# Check release status
helm status my-postgres -n databases

# Upgrade a release (new values or chart version)
helm upgrade my-postgres bitnami/postgresql \
  --set primary.persistence.size=50Gi \
  -n databases

# Rollback to previous revision
helm rollback my-postgres 1 -n databases

# View release history
helm history my-postgres -n databases

# Uninstall a release
helm uninstall my-postgres -n databases

Creating Your Own Chart

bash — Chart scaffolding
# Create chart structure
helm create my-web-app

# Chart directory structure:
# my-web-app/
#   Chart.yaml         # Chart metadata
#   values.yaml        # Default configuration values
#   templates/
#     deployment.yaml  # Deployment template
#     service.yaml     # Service template
#     ingress.yaml     # Ingress template
#     _helpers.tpl     # Template helpers
#     NOTES.txt        # Post-install instructions

# Test rendering without installing
helm template my-web-app ./my-web-app -f values-staging.yaml

# Lint the chart for errors
helm lint ./my-web-app

# Package the chart
helm package ./my-web-app

# Install from local chart
helm install my-release ./my-web-app -f values.yaml

When writing YAML for Helm values files and Kubernetes manifests, the YAML Editor validates your syntax in real time and catches indentation issues that cause cryptic Kubernetes errors.

Production Checklist

1) Set resource requests and limits on every container. 2) Use readiness and liveness probes. 3) Run at least 2 replicas. 4) Use PodDisruptionBudgets. 5) Store secrets in a secrets manager. 6) Set up HPA for autoscaling. 7) Use namespaces to isolate environments. 8) Enable RBAC.


Related Developer Tools


Frequently Asked Questions

Docker is a container runtime that builds and runs individual containers. Kubernetes is a container orchestration platform that manages many containers across multiple machines. Docker answers the question "how do I package and run my application in a container?" Kubernetes answers "how do I deploy, scale, and manage hundreds of containers across a cluster of servers?" You typically use Docker to build container images and Kubernetes to deploy and orchestrate them in production. They are complementary tools, not competitors.

A Pod is the smallest deployable unit in Kubernetes. It wraps one or more containers that share the same network namespace (they can communicate via localhost) and storage volumes. You do not deploy containers directly in Kubernetes because Pods provide a higher level of abstraction: they handle co-located containers that must run together, shared storage between containers, and initialization logic through init containers. In practice, most Pods run a single application container. Multi-container Pods are used for sidecar patterns like log collectors, service meshes (Istio/Envoy), or config reloaders.

Use a Service of type LoadBalancer or an Ingress resource. A LoadBalancer Service provisions a cloud load balancer (on AWS, GCP, or Azure) that routes external traffic to your Pods. An Ingress resource provides HTTP/HTTPS routing with path-based and host-based rules, SSL termination, and is more cost-effective because one Ingress controller (like Nginx or Traefik) can route traffic to many services. For development, use kubectl port-forward to access a Pod or Service from your local machine without any external exposure. For production, Ingress with a TLS certificate (often managed by cert-manager) is the standard approach.

Kubernetes supports both manual and automatic scaling. Manual scaling changes the replica count directly: kubectl scale deployment myapp --replicas=5. Horizontal Pod Autoscaler (HPA) automatically adjusts the number of Pod replicas based on CPU usage, memory usage, or custom metrics. For example, an HPA can scale from 2 to 10 replicas when average CPU usage exceeds 70%. Vertical Pod Autoscaler (VPA) adjusts the CPU and memory requests of individual Pods. Cluster Autoscaler adds or removes nodes (virtual machines) from the cluster when Pods cannot be scheduled due to insufficient resources. These three autoscalers can work together for fully automated scaling from application level to infrastructure level.

Helm is a package manager for Kubernetes. It bundles related Kubernetes manifests (Deployments, Services, ConfigMaps, etc.) into reusable packages called charts. Instead of managing dozens of YAML files manually, you install a chart with one command: helm install myrelease mychart. Helm handles templating (different values for staging vs production), versioning (rollback to a previous release), and dependency management (a chart can depend on other charts). You need Helm when you deploy complex applications with many Kubernetes resources, want to share standardized deployments across teams, or want to install third-party software (databases, monitoring tools, ingress controllers) without writing all the YAML yourself.

CrashLoopBackOff means the container keeps starting and crashing. First, check the logs: kubectl logs <pod-name> --previous (the --previous flag shows logs from the last crashed instance). Check the Pod events: kubectl describe pod <pod-name> and look at the Events section at the bottom for error messages like image pull failures, OOMKilled (out of memory), or failed health checks. Common causes include: missing environment variables or ConfigMaps, incorrect command or entrypoint in the container image, the application crashing due to a bug or unhandled exception, liveness probes failing too aggressively, and insufficient memory limits causing OOMKill. If the container exits too quickly to read logs, add a sleep command to the container spec temporarily: command: ['sh', '-c', 'sleep 3600'] to keep it running while you exec into it and investigate.

NT

Christian Bucher

QTool indexes 269 free tool pages. Many run entirely in the browser; pages that use public APIs or external libraries disclose that network boundary.

269 Developer Tools, Zero Signup

Browse 269 indexed tool pages with no QTool account required, and inspect the source on GitHub.

Open Source — Free Forever Try Free Tools

Related Tools

CSS Box Shadow Generator · Emoji Picker & Search · Free Git Diff Viewer

Related Tools

Free API Mock Server · Code Screenshot Generator - Beautiful Code Images · Free Color Palette Generator

Related Articles

Built by Miguel

Need a custom tool or website?

From . Delivered in 24-48h. You own the code.

View Services →