E-NO
Kubernetes local lab 9 Min Read

Kubernetes Local Lab Setup With Practical Examples

calendar_today Published: 2026-08-13
update Last Updated: 2026-08-13
analytics SEO Efficiency: 97%
Technical guide illustration for Kubernetes Local Lab Setup With Practical Examples.

A local Kubernetes lab is the fastest, safest place to explore cluster behavior, try configuration changes, and practice incident recovery without risking shared environments. This guide shows how to stand up a small, disposable lab; deploy simple workloads; verify with clear, observable checks; troubleshoot common failures; and tear everything down cleanly so you can repeat the cycle.

Practical scope is essential: start with a narrow, measurable pilot you can inspect locally end-to-end, then expand once the base is reliable. This article follows that path: you will build a two-node cluster, expose a service on a known port, verify traffic, change something visible, and roll it back. Along the way, you will capture version and environment details so repeats are predictable.

Version and Environment Inventory

Before creating a cluster, take a quick inventory. This avoids guesswork later and reduces rework when repeating the lab on a new machine.

  • Host OS: Linux, macOS, or Windows (WSL2 recommended on Windows)
  • CPU: 4 logical cores minimum for a smooth experience
  • RAM: 8 GB minimum; 16 GB recommended if you plan to run multiple services
  • Disk: 20 GB free (images and logs add up)
  • Virtualization: required for VM-based tools; not needed for container-based clusters
  • Networking: ensure localhost ports you plan to use (for example 8080, 8443) are free

Install these tools:

  • kubectl (client CLI)
  • One local Kubernetes tool. This guide uses kind (Kubernetes in containers) for speed and ease of teardown. Minikube is a solid VM-based alternative if you prefer VM isolation.

Verify your toolchain after install:

# Verify kubectl client version
kubectl version --client --output=yaml

# If using kind
kind version

# If using minikube
minikube version

If a command is missing, fix PATH or complete the installation before proceeding.

Choosing a Local Lab Option

The numbers in the table are constructed example values to help you reason about tradeoffs on a typical developer laptop.

OptionRuntimeIsolationTypical RAM GBStart time secBest forSource
kindContainersProcess2-420-60Fast, disposable clustersConstructed example
minikubeVM or cont.VM/proc3-630-120VM isolation, optional driversConstructed example
k3dContainersProcess2-415-45Lightweight, multi-cluster setupsConstructed example

Pick one, write down the version, and keep using it consistently for the rest of this guide.

Safe Configuration Path

To keep the lab simple and debuggable, use kind with explicit port mappings and a dedicated namespace. We will:

  • Create a two-node cluster (1 control-plane, 1 worker)
  • Pre-map NodePort 30080 to localhost:8080 and NodePort 30443 to localhost:8443
  • Work in a single namespace: lab

Why map NodePorts? It avoids the extra moving parts of an Ingress controller while still testing service exposure from your host.

1) Create the Kind Cluster

Create a file named kind-lab.yaml with this content:

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: lab
nodes:
 - role: control-plane
   kubeadmConfigPatches:
   - |
     kind: InitConfiguration
     nodeRegistration:
       kubeletExtraArgs:
         node-labels: "ingress-ready=true"
   extraPortMappings:
   - containerPort: 30080
     hostPort: 8080
     protocol: TCP
   - containerPort: 30443
     hostPort: 8443
     protocol: TCP
 - role: worker

Create the cluster:

kind create cluster --config kind-lab.yaml
kubectl cluster-info
kubectl get nodes -o wide

You should see Ready status on both nodes. If not, see Failure Modes and Recovery below.

Create and use the namespace for isolation:

kubectl create namespace lab
kubectl config set-context --current --namespace=lab

Practical Examples

You will deploy an NGINX web server fronted by a NodePort Service. Then you will customize the content with a ConfigMap and perform a rolling update.

Example 1: NGINX Deployment + NodePort Service

Create a file named web.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  labels:
    app: web
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: nginx
        image: nginx:stable
        ports:
        - containerPort: 80
        readinessProbe:
          httpGet:
            path: /
            port: 80
          periodSeconds: 5
        livenessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 10
          periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  type: NodePort
  selector:
    app: web
  ports:
  - name: http
    port: 80
    targetPort: 80
    nodePort: 30080

Apply and verify:

kubectl apply -f web.yaml
kubectl rollout status deployment/web
kubectl get svc web -o wide

Test from the host using the mapped port:

curl -sSf http://localhost:8080/ | head -n 5

Expected: NGINX default HTML content and HTTP 200.

Scale up to observe a rolling effect:

kubectl scale deploy/web --replicas=3
kubectl get pods -l app=web -w

Example 2: Customize Content With a ConfigMap and Rolling Update

Create a ConfigMap with an index.html and mount it into NGINX.

Create a file named web-custom.yaml:

apiVersion: v1
kind: ConfigMap
metadata:
  name: web-content
  labels:
    app: web
data:
  index.html: |
    <!doctype html>
    <html>
    <head><title>Lab Web</title></head>
    <body>
    <h1>Constructed example: Kubernetes lab web</h1>
    <p>Served by NGINX from a ConfigMap.</p>
    </body>
    </html>
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  labels:
    app: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: nginx
        image: nginx:stable
        ports:
        - containerPort: 80
        volumeMounts:
        - name: web-root
          mountPath: /usr/share/nginx/html/index.html
          subPath: index.html
        readinessProbe:
          httpGet:
            path: /
            port: 80
          periodSeconds: 5
        livenessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 10
          periodSeconds: 10
      volumes:
      - name: web-root
        configMap:
          name: web-content
          items:
          - key: index.html
            path: index.html

Apply and watch the rollout:

kubectl apply -f web-custom.yaml
kubectl rollout status deployment/web

Verify the content change:

curl -sSf http://localhost:8080/ | grep -F "Constructed example: Kubernetes lab web"

Roll back to the previous ReplicaSet if needed:

kubectl rollout undo deployment/web

Verification and Diagnostics

Systematic checks help you catch issues early.

Cluster Health

# Nodes Ready
kubectl get nodes

# Core components in kube-system
kubectl get pods -n kube-system -o wide

# Events if something is Pending or CrashLoopBackOff
kubectl get events --sort-by=.lastTimestamp | tail -n 20

Expected: all nodes show Ready; kube-proxy, CoreDNS pods Running; no repeating crash loops in events.

Workload and Networking

# Pods and Services in lab namespace
kubectl get deploy,rs,po,svc -n lab -o wide

# Describe a pod to check readiness/liveness and image pulls
kubectl describe pod -n lab -l app=web | sed -n '1,80p'

# Test NodePort mapping from host
curl -I http://localhost:8080/

Expected: HTTP/1.1 200 OK and Server: nginx in headers.

Logs and Rollout Details

# Logs from a pod (pick one from kubectl get pods)
POD="$(kubectl get pods -n lab -l app=web -o jsonpath='{.items[0].metadata.name}')"
kubectl logs -n lab "$POD" --tail=50

# Rollout history and status
kubectl rollout history deployment/web
kubectl rollout status deployment/web

Failure Modes and Recovery

Use this quick matrix to triage common issues.

SymptomLikely causeCommand to checkRemediationSource
Nodes NotReadyContainer runtime not updocker ps OR podman psStart runtime; recreate cluster if neededConstructed example
Pods PendingInsufficient resourceskubectl describe pod -n lab <pod>Reduce replicas; free RAM/CPU; rescheduleConstructed example
CrashLoopBackOff in CoreDNSCNI or DNS config issuekubectl logs -n kube-system <coredns-pod>Recreate cluster; ensure default CNI activeConstructed example
Service not reachable on 8080Port conflict or NodePortss -lntp | grep 8080; kubectl get svc web -o yamlFree host port; ensure nodePort is 30080Constructed example
ImagePullBackOffNetwork or image namekubectl describe pod -n lab <pod>Fix image name; confirm host internet accessConstructed example
Stuck rolloutBad readiness probeskubectl describe deploy/webCorrect probes; kubectl rollout undoConstructed example

Fast Rollback and Full Reset

If you only need to revert the last change:

# Undo last rollout of the web deployment
kubectl rollout undo deployment/web -n lab

# Or revert the content by deleting the ConfigMap so pods use baked-in defaults
kubectl delete configmap web-content -n lab
kubectl rollout restart deployment/web -n lab

If the cluster is in a bad state, delete and recreate it. This is a lab; rebuilds are cheap.

# Tear down the cluster
kind delete cluster --name lab

# Optional: free disk by pruning images (use with care)
docker system prune -f
# or, if using podman
podman system prune -f

# Recreate fresh
kind create cluster --config kind-lab.yaml
kubectl get nodes

After a recreate, reapply your manifests:

kubectl create namespace lab
kubectl config set-context --current --namespace=lab
kubectl apply -f web.yaml
# or apply the customized version
yaml_to_apply=web.yaml
[ -f web-custom.yaml ] && yaml_to_apply=web-custom.yaml
kubectl apply -f "$yaml_to_apply"

Operations Checklist

Use this as a repeatable runbook.

  • Record environment: OS, CPU, RAM, disk, runtime, kubectl, and kind versions
  • Ensure host ports 8080 (HTTP) and 8443 (HTTPS) are free
  • Create cluster: kind create cluster --config kind-lab.yaml
  • Verify nodes Ready: kubectl get nodes; check kube-system pods
  • Create namespace: kubectl create namespace lab; set current context
  • Deploy workload: kubectl apply -f web.yaml
  • Verify rollout: kubectl rollout status deployment/web
  • Test service: curl http://localhost:8080/
  • Make a safe change: apply web-custom.yaml; validate content; observe rollout
  • If something breaks: kubectl describe, logs, events; kubectl rollout undo
  • Capture notes: versions, manifests, observed outputs, and fixes
  • Tear down when done: kind delete cluster --name lab

Notes on Extending the Lab Safely

Once the base is solid and fast to rebuild, you can expand in small increments:

  • Add HTTPS: map NodePort 30443 to localhost:8443 and front an app with TLS termination in your app or via a lightweight proxy
  • Add Ingress: install an Ingress controller and switch Services to ClusterIP; verify DNS mapping via hosts file if needed
  • Add storage: introduce a simple local storage provisioner and test a StatefulSet using a small PersistentVolumeClaim
  • Add observability: install metrics-server for kubectl top and a small Prometheus stack; confirm targets and scrape data

Add one component at a time, verify, and document expected signals before moving to the next.

Conclusion

You now have a practical, disposable Kubernetes lab that you can create, verify, break on purpose, and rebuild in minutes. By scoping the setup, mapping a known NodePort to localhost, and focusing on observable examples, you reduce time-to-signal and make it easy to practice real operations tasks: rollouts, rollbacks, scaling, and diagnostics. The two-node kind cluster with pre-mapped ports gives you a realistic control-plane-plus-worker topology without the overhead of a full VM stack. Each example in this guide produces a visible result you can curl from your host, so you always know whether a change succeeded or failed. Capture your manifests in version control, tag known-good baselines, and rehearse common failure scenarios until recovery becomes routine. A reliable local lab turns experimentation into a safe, repeatable habit that carries forward into production confidence.

Related Research

Article Quality Score

Reader usefulness 97%
  • check_circle Reader-ready guide
  • check_circle Practical examples included
  • check_circle Clean SEO article URL