Warrior, now we move from Kubernetes theory โ actually running Kubernetes on your own PC.
Since you're on Windows and already work with Docker/Node.js, we'll focus on a practical local setup.
Before deploying to:
you should be able to run:
Your PC
โ
Local Kubernetes Cluster
โ
Node
โ
Pod
โ
Container
โ
Your applicationThis lets you practice Kubernetes without paying for cloud infrastructure.
There are three common options:
| Tool | Best for |
|---|---|
| Docker Desktop Kubernetes | Easiest for beginners |
| Minikube | Learning Kubernetes |
| Kind | Kubernetes testing/CI |
Conceptually:
Docker Desktop
โ
Kubernetes clusterMinikube
โ
Kubernetes clusterKind
โ
Kubernetes nodes inside containersFor your learning journey:
is the easiest starting point.
Why?
You already understand Docker.
You can have:
Windows
โ
โ
Docker Desktop
โ
โ
Kubernetes
โ
โโโ Control Plane
โ
โโโ Worker NodeFor more realistic multi-node testing later, Kind is excellent.
Docker Desktop can provide a local Kubernetes cluster.
After installing Docker Desktop, open:
Docker Desktop
โ
Settings
โ
KubernetesDepending on your Docker Desktop version, the exact UI wording may differ.
Enable Kubernetes and create/start the cluster.
Open PowerShell:
kubectl version --clientYou should get client information.
Then:
kubectl cluster-infoIf Kubernetes is running correctly, you'll see cluster/control-plane information.
Run:
kubectl get nodesYou might see:
NAME STATUS ROLES AGE VERSION
docker-desktop Ready control-plane 10m v1.xx.xThe important part:
STATUS = Readymeans your node is available for scheduling.
Your laptop is now effectively hosting a Kubernetes cluster.
Conceptually:
YOUR WINDOWS PC
โ
Docker Desktop
โ
Kubernetes Cluster
โ
โโโโโโโโดโโโโโโโ
โ โ
Control Plane Worker
Node
โ
Pod
โ
ContainerDepending on the local distribution/version, control-plane and worker roles may be implemented differently. Don't assume every local setup exactly mirrors a multi-node cloud cluster.
Run:
kubectl get pods -AYou will probably see system components in namespaces such as:
kube-systemFor example:
coredns
kube-proxyThese are Kubernetes infrastructure components.
Let's create an NGINX Pod.
kubectl run nginx --image=nginxCheck:
kubectl get podsYou might initially see:
NAME READY STATUS RESTARTS AGE
nginx 0/1 ContainerCreating 0 2sWait a moment.
Then:
kubectl get podsEventually:
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 30sCongratulations.
You just created a Kubernetes workload.
You ran:
kubectl run nginx --image=nginxThe flow is approximately:
kubectl
โ
โ
API Server
โ
โ
Pod object created
โ
โ
Scheduler
โ
โ
Node selected
โ
โ
kubelet
โ
โ
Container runtime
โ
โ
NGINX containerThis is the architecture from Chapter 3 happening for real.
Run:
kubectl describe pod nginxLook at:
EventsYou may see events similar to:
Scheduled
Pulling
Pulled
Created
StartedThis is extremely useful for debugging.
Run:
kubectl get pod nginx -o wideExample:
NAME READY STATUS IP NODE
nginx 1/1 Running 10.1.0.15 docker-desktopThe Pod has its own cluster IP.
But remember:
Pod IPs are not meant to be your stable application endpoint.
That's why we learned Services.
Expose NGINX internally:
kubectl expose pod nginx --port=80Check:
kubectl get servicesYou should see something like:
NAME TYPE CLUSTER-IP PORT(S)
kubernetes ClusterIP 10.x.x.x 443/TCP
nginx ClusterIP 10.x.x.x 80/TCPBecause:
ClusterIPis normally accessible only inside the Kubernetes cluster.
Your browser is outside the cluster network.
Architecture:
Browser
X
โ
โ
ClusterIP
โ
โ
PodWe need an external access method.
Delete the current Service:
kubectl delete service nginxNow create:
kubectl expose pod nginx `
--port=80 `
--target-port=80 `
--type=NodePortCheck:
kubectl get service nginxExample:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
nginx NodePort 10.x.x.x <none> 80:30080/TCPThe important part is:
80:30080Meaning:
Service port = 80
NodePort = 30080Depending on your local Kubernetes implementation, you may be able to access:
http://localhost:30080If that doesn't work with your specific local setup, inspect the cluster's networking behavior or use the convenient method in the next section.
kubectl port-forward#This is one of the most useful local-development commands.
Run:
kubectl port-forward pod/nginx 8080:80You should see:
Forwarding from 127.0.0.1:8080 -> 80Now open:
http://localhost:8080You'll see the NGINX page.
The architecture is:
Browser
โ
โ localhost:8080
โ
kubectl port-forward
โ
โ
Pod:80
โ
โ
NGINXSuppose you have:
PostgreSQL
Redis
Node.js API
Grafana
Prometheusrunning inside Kubernetes.
You can temporarily access them locally.
For example:
kubectl port-forward svc/backend 3000:80Then:
localhost:3000
โ
backend Service
โ
backend PodThis is excellent for development and debugging.
kubectl port-forward is primarily a development/debugging mechanism.
Production architecture is normally:
Internet
โ
Load Balancer / Ingress
โ
Service
โ
PodsNot:
Internet
โ
kubectl port-forwardEarlier we created:
kubectl run nginx --image=nginxThat's a direct Pod.
Production applications usually use:
Deployment
โ
ReplicaSet
โ
PodsCreate one:
kubectl create deployment nginx `
--image=nginxCheck:
kubectl get deploymentsThen:
kubectl get podsRun:
kubectl scale deployment nginx --replicas=3Check:
kubectl get podsYou'll see three Pods:
nginx-xxxxx
nginx-yyyyy
nginx-zzzzzArchitecture:
Deployment
โ
ReplicaSet
โโโโโโโโโผโโโโโโโโ
โ โ โ
Pod Pod PodCreate a Service:
kubectl expose deployment nginx `
--port=80 `
--type=NodePortCheck:
kubectl get svcNow:
Browser
โ
NodePort
โ
Service
โ
โโโโโโโโฌโโโโโโโฌโโโโโโโ
Pod Pod PodThe Service distributes traffic across eligible Pods.
Now let's stop using imperative commands.
Create:
deployment.yamlapiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80Apply:
kubectl apply -f deployment.yamlCheck:
kubectl get deployment
kubectl get podsCreate:
service.yamlapiVersion: v1
kind: Service
metadata:
name: nginx
spec:
selector:
app: nginx
ports:
- port: 80
targetPort: 80
type: NodePortApply:
kubectl apply -f service.yamlCheck:
kubectl get svc nginxOur Deployment:
labels:
app: nginxService:
selector:
app: nginxThe Service finds Pods using the label.
Pod
labels:
app=nginx
โ
โ
โ matches
โ
Service
selector:
app=nginxIf you accidentally write:
selector:
app: backendthe Service won't find those Pods.
Then:
Service
โ
No endpointsThis is one of the most common Kubernetes mistakes.
Run:
kubectl get endpoints nginxor, on modern clusters:
kubectl get endpointslicesIf your Service has three ready Pods, you should see corresponding backend endpoints.
This is a great debugging technique.
Now let's make this relevant to your stack.
Suppose your Node.js API is:
const express = require("express");
const app = express();
app.get("/", (req, res) => {
res.json({
message: "Hello Kubernetes"
});
});
app.get("/health", (req, res) => {
res.json({
status: "ok"
});
});
app.listen(3000, "0.0.0.0", () => {
console.log("Server running on port 3000");
});Notice:
app.listen(3000, "0.0.0.0")This matters.
Inside containers, don't accidentally bind the server only to:
127.0.0.1because then other network interfaces may not be able to reach it.
Dockerfile:
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]Build:
docker build -t node-api:1.0 .Now you have:
node-api:1.0Here's something beginners often encounter.
You built:
node-api:1.0on your machine.
But Kubernetes might use a container runtime/environment where that locally built image isn't automatically available in the way you expect.
Then you get:
ErrImagePullor:
ImagePullBackOffWhy?
Kubernetes may try to pull:
node-api:1.0from a registry.
But your image exists only locally.
You have several options.
Depending on your Docker Desktop configuration, locally built images may be available to the local cluster.
This is one reason Docker Desktop is convenient for beginners.
You can run a local container registry:
Your machine
โ
โโโ Docker build
โ
โ
Local Registry
โ
โ
KubernetesKind supports workflows where images can be loaded into the cluster.
Conceptually:
docker build -t node-api:1.0 .then:
kind load docker-image node-api:1.0Now Kind nodes can use that image.
For example:
Docker Hub
ECR
GHCR
GCR/Artifact Registry
ACRThen Kubernetes pulls it.
Production typically uses a registry.
Example:
apiVersion: apps/v1
kind: Deployment
metadata:
name: node-api
spec:
replicas: 2
selector:
matchLabels:
app: node-api
template:
metadata:
labels:
app: node-api
spec:
containers:
- name: node-api
image: node-api:1.0
ports:
- containerPort: 3000
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "512Mi"
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 10This combines concepts you've already learned:
Deployment
+
replicas
+
resources
+
readinessProbeapiVersion: v1
kind: Service
metadata:
name: node-api
spec:
selector:
app: node-api
ports:
- port: 80
targetPort: 3000
type: ClusterIPNotice:
Service: 80
Pod: 3000So:
node-api:80
โ
Pod:3000Because it's a ClusterIP Service, create a temporary debugging Pod:
kubectl run curl `
--image=curlimages/curl `
-it `
--rm `
--restart=Never `
-- shInside:
curl http://node-apiYou should receive:
{
"message": "Hello Kubernetes"
}Now you've actually tested Kubernetes Service discovery.
Inside the debug container:
nslookup node-apiYou'll see the Kubernetes DNS resolve the Service.
This demonstrates:
node-api
โ
CoreDNS
โ
ClusterIPAt this point you can build:
Browser
โ
โ
NodePort
โ
โ
Frontend Service
โ
โ
Next.js Pods
โ
โ
Backend Service
โ
โ
Node.js Pods
โ โ
โ โ
Redis PostgreSQLAnd eventually:
Ingress
โ
โโโโโโโโโโดโโโโโโโโโ
โ โ
Next.js Service API Service
โ โ
Next.js Pods Node Pods
โ
โโโโโโโดโโโโโโ
โ โ
Redis PostgreSQLYou may already know Docker Compose.
Compare:
docker-compose.yml
frontend
backend
redis
postgresDeployment
Service
ConfigMap
Secret
Ingress
PVC
HPA
NetworkPolicyCompose is usually simpler for local multi-container development.
Kubernetes is designed for orchestration and production-scale management.
Now let's understand Minikube.
Minikube is a tool that runs a local Kubernetes cluster.
Typical architecture:
Windows
โ
Minikube
โ
KubernetesYou can check:
minikube statusStart:
minikube startCheck:
kubectl get nodesMinikube can provide a dashboard:
minikube dashboardThis can help beginners visually understand:
Namespaces
Deployments
Pods
Services
ConfigMaps
SecretsHowever, don't rely entirely on the UI.
You should become comfortable with:
kubectlbecause that's what you'll use heavily in real environments.
Kind means:
Kubernetes IN Docker
It runs Kubernetes nodes as containers.
Architecture:
Docker
โ
โโโ control-plane container
โโโ worker container
โโโ worker containerCreate:
kind create clusterCheck:
kubectl get nodesKind is particularly useful for:
For your learning:
Docker Desktop
โ
easiest beginningMinikube
โ
Kubernetes learningKind
โ
multi-node/testing/CIYou don't need to install all three immediately.
Start with one.
You should understand this entire sequence:
1. Write Node.js application
โ
2. Dockerfile
โ
3. docker build
โ
4. Docker image
โ
5. Kubernetes Deployment
โ
6. Pod
โ
7. Service
โ
8. port-forward / NodePort / Ingress
โ
9. Browser/clientAnd if you use a registry:
Docker image
โ
Container Registry
โ
Kubernetes
โ
Podkubectl cluster-info
kubectl get nodes
kubectl get nodes -o widekubectl get pods
kubectl get pods -o wide
kubectl describe pod <pod>
kubectl logs <pod>kubectl get deployments
kubectl describe deployment <deployment>
kubectl scale deployment <deployment> --replicas=3kubectl get svc
kubectl describe svc <service>kubectl get events --sort-by=.lastTimestamp
kubectl exec -it <pod> -- /bin/shkubectl port-forward pod/<pod> 8080:80or:
kubectl port-forward svc/<service> 3000:80When something doesn't work:
Application not working
โ
kubectl get pods
โ
Is Pod Running?
/ \
NO YES
โ โ
describe Service?
logs โ
events Endpoint?
โ
Ingress?
โ
DNS?This connects directly to Chapter 23.
Build this:
Node.js API
โ
โ
Deployment
โ
โโโ Pod 1
โโโ Pod 2
โ
โ
Service
โ
โ
localhostRequirements:
GET /returns:
{
"message": "Hello Kubernetes"
}GET /healthreturns:
{
"status": "ok"
}Use:
Then access it with:
kubectl port-forward svc/node-api 3000:80Open:
http://localhost:3000If you can do this successfully, you've gone from:
Kubernetes theory
โ
Running Kubernetes applicationDocker Desktop
โ
Local Kubernetes
kubectl get nodes
โ
Check cluster
kubectl get pods
โ
Check workloads
kubectl describe
โ
Inspect details/events
kubectl logs
โ
Application logs
Deployment
โ
Manages Pods
Service
โ
Stable networking
ClusterIP
โ
Internal access
NodePort
โ
External node-level access
port-forward
โ
Local development/debugging
Ingress
โ
HTTP/HTTPS routing
Minikube
โ
Local Kubernetes learning
Kind
โ
Kubernetes-in-Docker / testing LOCAL PC
โ
Docker
โ
Kubernetes
โ
Deployment
โ
ReplicaSet
โ
โโโโโโโโโโดโโโโโโโโโ
โ โ
Pod Pod
โ โ
Container Container
โโโโโโโโโโฌโโโโโโโโโ
โ
Service
โ
Local ClientThis is where local Kubernetes becomes real cloud Kubernetes.
We'll cover:
Developer
โ
Docker
โ
ECR
โ
EKS
โ
Deployment
โ
Pods
โ
Service
โ
AWS Load Balancer
โ
InternetMark it as complete to track your mastery.