Every cook needs a kitchen, and before we run a single real Kubernetes recipe, we need a cluster we can afford to break. This first part of the series builds that kitchen: a throwaway cluster running on your own laptop with kind, the kubectl command-line tool to talk to it, and your very first Pod serving an HTTP response. By the end, you will be able to spin the whole thing up, wreck it, delete it, and rebuild it in under a minute — which is exactly the confidence you need for everything that follows.

What you need

The ingredients list for this entire part is short:

  • Docker Desktop (macOS or Windows) or Docker Engine (Linux) installed and running. kind stands for "Kubernetes in Docker," so Docker is the only real prerequisite. Check it with docker version — if that prints a client and server version, you are good.
  • About 15 minutes and a comfort with copy-pasting terminal commands.

Everything else — kubectl, kind, the cluster itself — we install fresh below.

Recipe 1: Install kubectl and kind

Problem: You need two tools before anything else works: kubectl, the command-line client that talks to every Kubernetes cluster, and kind, which builds a local cluster out of Docker containers.

On Linux, download both as single static binaries:

# kubectl — pin a recent stable version or fetch the latest one curl -LO "https://dl.k8s.io/release/$(curl -Ls https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl # kind — single binary, same idea curl -Lo ./kind "https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64" sudo install -o root -g root -m 0755 kind /usr/local/bin/kind 

On macOS, Homebrew does it in two lines:

brew install kubectl brew install kind 

Now verify both installations:

kubectl version --client kind version 
Client Version: v1.31.2 Kustomize Version: v5.4.2 kind v0.26.0 go1.23.4 darwin/arm64 

kubectl version --client deliberately asks for only the client version — there is no cluster yet, so asking for the server version would fail.

How it works: kubectl is a single self-contained binary that speaks the Kubernetes REST API. It does not care where the cluster lives — your laptop, a cloud, a data center — as long as it has credentials. kind is a tiny wrapper that bootstraps a whole Kubernetes cluster inside one or more Docker containers on your machine, which is why it is fast, free, and disposable.

Variations:

  • Prefer a virtual machine over Docker containers? minikube (brew install minikube, or a binary from minikube.sigs.k8s.io) is the other popular local cluster. Every recipe in this series works on minikube too; just swap kind create cluster --name cookbook for minikube start -p cookbook.
  • On Windows, use Docker Desktop plus winget install -e --id Kubernetes.kubectl and winget install -e --id derailed.k9s-style installs, or grab the kind binary from its GitHub releases page.

Recipe 2: Create the playground

Problem: The tools are installed, but there is no cluster to talk to yet. Time to build one.

kind create cluster --name cookbook 
Creating cluster "cookbook" ... ✓ Ensuring node image (kindest/node:v1.31.x) ✓ Preparing nodes ✓ Writing configuration ✓ Starting control-plane ✓ Installing CNI ✓ Installing StorageClass Set kubectl context to "kind-cookbook" 

Wait about 30-60 seconds, then confirm the cluster answers:

kubectl cluster-info kubectl get nodes 
Kubernetes control plane is running at https://127.0.0.1:51234 NAME STATUS ROLES AGE VERSION cookbook-control-plane Ready control-plane 90s v1.31.2 

kind also quietly updated your kubeconfig — the file at ~/.kube/config that stores cluster addresses and credentials, organized into named contexts. Look at it:

kubectl config get-contexts kubectl config current-context 
CURRENT NAME CLUSTER AUTHINFO NAMESPACE * kind-cookbook kind-cookbook kind-cookbook 

How it works: A Kubernetes cluster has two kinds of machines. The control plane is the brain: it stores the desired state of everything, schedules work, and answers the API that kubectl calls. Worker nodes are the muscle: they actually run your containers. In this kind cluster both roles live in one Docker container called cookbook-control-plane (that's the "ROLES: control-plane" above) — perfect for learning, never for production. The most important idea to absorb already: Kubernetes is declarative. You do not tell it how to do things step by step; you describe the state you want ("run one copy of this image") and the control plane continuously works to make reality match. Every recipe in this series is just a different way of describing a desired state.

When it goes wrong:

  • docker: permission denied — your Linux user is not in the docker group. Run sudo usermod -aG docker $USER and log back in.
  • Port already in use — kind maps the API server to a random local port, but if you have several kind clusters, check docker ps and kind delete cluster --name <old>.

Recipe 3: Run your first Pod imperatively

Problem: A running cluster with nothing on it is boring. Let's start the smallest unit Kubernetes runs — a Pod — with a single command, and actually reach it over HTTP.

kubectl run hello --image=hashicorp/http-echo -- -text="hello from clinerds" 
pod/hello created 

kubectl run creates a Pod named hello from the tiny hashicorp/http-echo image, which replies to every HTTP request with the text you pass it. One quirk: http-echo listens on port 5678 inside the container, not 80.

Check it is alive:

kubectl get pods 
NAME READY STATUS RESTARTS AGE hello 1/1 Running 0 20s 

Now reach it. Pods get internal cluster IPs that are not routable from your laptop, so use kubectl port-forward to tunnel a local port into the Pod:

kubectl port-forward pod/hello 8080:5678 

Leave that running, open a second terminal, and:

curl localhost:8080 
hello from clinerds 

Ctrl+C stops the port-forward; the Pod keeps running.

How it works: A Pod is one or more containers that share a network and get scheduled together — the atom of Kubernetes. Every Pod gets its own IP address, but only inside the cluster's private network. port-forward is a debugging bridge: kubectl opens a tunnel through the API server straight to your Pod, forwarding localhost:8080 to the container's port 5678. It is not how real traffic reaches apps (that is Services, coming in Part 5), but it is the fastest way to prove a Pod works.

Recipe 4: Same Pod, the declarative way

Problem: kubectl run worked, but one-off commands are impossible to review, version, or repeat. Real Kubernetes work happens in YAML files.

Delete the imperative Pod and write a manifest instead:

kubectl delete pod hello 

Create a file named hello-pod.yaml:

apiVersion: v1 kind: Pod metadata: name: hello labels: app: hello spec: containers: - name: echo image: hashicorp/http-echo args: ["-text=hello from clinerds"] ports: - containerPort: 5678 

Apply it and watch:

kubectl apply -f hello-pod.yaml kubectl get pods kubectl describe pod hello 
pod/hello created NAME READY STATUS RESTARTS AGE hello 1/1 Running 0 10s 

kubectl describe prints the Pod's full story — image, state, IP, and the Events section at the bottom, which is the first place to look when anything misbehaves.

Clean up with the same file:

kubectl delete -f hello-pod.yaml 

How it works: kubectl apply sends the manifest to the API server, which stores it as the desired state and starts making it real. The manifest has four parts you will see in every Kubernetes file: apiVersion and kind say what the object is, metadata names it and attaches labels (key-value tags used for selecting things later), and spec is the desired state itself. YAML beats imperative commands for three reasons: the file is reviewable in git, apply is idempotent (run it twice, nothing changes the second time), and the file is the documentation of what should be running. From Part 2 onward, this series is YAML-only.

Recipe 5: The kubectl survival kit, then burn it all down

Problem: You will live in kubectl for the rest of this series. Learn the five commands that answer 90% of questions, then prove the playground is disposable by deleting the entire cluster.

Recreate a Pod so we have something to poke at (kubectl apply -f hello-pod.yaml again), then run the survival kit:

# What exists? kubectl get pods # More columns: which node, which IP kubectl get pods -o wide # The full object, exactly as the API server stores it kubectl get pod hello -o yaml # Why is it in this state? Events live here. kubectl describe pod hello # What did the container print? kubectl logs hello # What fields does a Pod spec even have? Built-in docs. kubectl explain pod.spec.containers 

kubectl explain is the quiet superpower: it works offline, for every resource type and field, and saves you a trip to the docs. Try kubectl explain pod.spec --recursive | less to see the whole tree.

Now the finale — destroy the kitchen:

kind delete cluster --name cookbook kubectl get nodes 
Deleting cluster "cookbook" ... The connection to the server 127.0.0.1:51234 was refused 

Everything — the cluster, the Pod, the state — is gone. And getting it all back is one command:

kind create cluster --name cookbook 

How it works: kind delete cluster removes the Docker containers that were the cluster; there is no hidden state anywhere else. This is the whole point of the local playground: the cost of a mistake is kind delete plus 60 seconds. Treat experiments as free, because they are.

When it goes wrong:

  • kubectl hangs or refuses connections after a laptop sleep — kind clusters occasionally need docker start cookbook-control-plane after Docker restarts.
  • kubectl config get-contexts still lists a dead cluster — harmless, but clean it with kubectl config delete-context kind-cookbook.

Where to go next

You now have a cluster you can break without consequences, the two tools every Kubernetes recipe needs, and one Pod under your belt. In Part 2: Pods, we take Pods seriously — multi-container Pods, health probes, and resource limits. The full series lives at Kubernetes Cookbook, and if you want to go beyond a local sandbox someday, the official cluster setup docs cover every flavor from minikube to managed cloud.