You now know what Kubernetes does, how it is built inside and what every term in the vocabulary means. All of that was necessary theory, but Kubernetes is not learned by reading: it is learned by breaking things in a cluster of your own. This lesson has two parts. First, an honest overview of the real options for getting hold of a cluster —local, self-managed or managed in the cloud— with criteria for choosing in a professional project. And then, the step-by-step practical guide to building the practice cluster you will use throughout the twelve modules of the course, where we will deploy the Rutas Norte platform. By the end you will have a working environment, you will know how to stop it, resume it and destroy it without fear, and you will know the causes of the most common start-up failures.

Contents

  1. The three routes to getting a cluster
  2. The course practice cluster: requirements
  3. Installing minikube and the driver
  4. Starting the cluster with enough resources
  5. Addons required for the course
  6. Verifying the cluster
  7. Lifecycle: stop, resume, destroy
  8. The kind alternative and a multi-node cluster
  9. Troubleshooting common start-up failures

  1. The three routes to getting a cluster

Route Tools Who operates the control plane Cost When to choose it
Local minikube, kind, k3d, Docker Desktop You, but it is disposable Only your machine Learning, developing, testing manifests and CI
Self-managed kubeadm, k3s, Rancher, vendor distributions You, for real: patches, certificates, etcd, backups Servers + people Your own data centre, legal data-location requirements, total control
Managed in the cloud EKS (AWS), AKS (Azure), GKE (Google) The provider Control plane fee + nodes Most companies in production

Practical criteria for deciding:

  • Are you learning or developing? Local. No argument: it is free, quick to recreate and it exposes nothing.
  • Do you have a platform team with 24×7 on-call? If the answer is no, avoid self-managing production. Maintaining etcd, rotating certificates and upgrading three times a year is not a small job.
  • Are there data sovereignty constraints or special hardware? Self-managed, with kubeadm or a commercial distribution.
  • Ordinary production for an ordinary company? Managed. Rutas Norte will end up here: the control plane is the provider's problem and the team concentrates on the platform.

Each route has its own lesson later on: Minikube and Local Environments with kind, Kubeadm and Managed Kubernetes: EKS, AKS and GKE. Here we only build what we need to have a working environment.

Why minikube for this course

We choose minikube as the main option because, through its addons, it ships two things we will need a lot: an Ingress Controller (module 4) and a dynamic storage provisioner (module 5). Installing those by hand in other tools is extra work that teaches nothing at this point. We also give the equivalent recipe with kind, which is lighter and makes multi-node clusters easy.

  1. The course practice cluster: requirements

Rutas Norte will end up with six components deployed at once plus the observability add-ons, so it is worth not skimping on resources.

Resource Minimum Recommended for the whole course
CPU 2 cores 4 cores
RAM 4 GiB free 8 GiB free (16 GiB on the machine)
Disk 20 GiB free 40 GiB free
Operating system Linux, macOS or Windows 10/11 with WSL2 Linux or macOS
Virtualisation Docker, or a hypervisor (KVM, HyperKit, Hyper-V) Docker as the driver
Network Internet access to pull images

You also need kubectl, whose installation and use are covered in detail in the next lesson, The Kubernetes CLI: kubectl. If you do not have it yet, minikube can run it for you with minikube kubectl -- <command>, although the normal thing is to install it separately.

  1. Installing minikube and the driver

3.1. The driver

minikube creates the cluster inside something: a container or a virtual machine. That "something" is the driver.

Driver Platforms Advantages Drawbacks
docker (recommended) Linux, macOS, Windows Fast, no hypervisor, starts in seconds The node is a container: less isolated
kvm2 Linux Real VM isolation Requires KVM and permissions
hyperkit / vfkit macOS Lightweight VM Less used today
hyperv Windows Pro/Enterprise Built into the system Requires the Pro edition
none Linux Installs onto the machine itself Modifies your system; only for dedicated servers

We will use docker. Check that you have it and that your user can use it without sudo:

docker version --format '{{.Server.Version}}'
docker run --rm hello-world

If the second command fails with permission denied, add your user to the docker group and log in again:

sudo usermod -aG docker $USER && newgrp docker

3.2. Installing minikube

Linux (x86_64):

curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
rm minikube-linux-amd64

macOS (with Homebrew):

brew install minikube

Windows (with winget, in PowerShell):

winget install Kubernetes.minikube

Verify the installation:

minikube version
minikube version: v1.33.1
commit: 5883c09216182566a63dff4c326a6fc9ed2982ff

  1. Starting the cluster with enough resources

This is the command that defines your working environment for the whole course:

minikube start \
  --profile=rutas-norte \
  --driver=docker \
  --kubernetes-version=v1.30.0 \
  --cpus=4 \
  --memory=8192 \
  --disk-size=40g

What each option does, and why:

Option Meaning Why like this
--profile=rutas-norte The cluster and context name Lets you keep several independent clusters on the same machine without clashing with other work
--driver=docker Where the node is created Fast and with no hypervisor
--kubernetes-version=v1.30.0 A pinned version The course uses 1.30+; pinning it stops a future minikube start changing the behaviour
--cpus=4 Cores assigned Enough for the six components plus module 7's Prometheus
--memory=8192 MiB of RAM Below 4096 you will start to see pods evicted
--disk-size=40g The node's disk Images and persistent volumes take up space

Expected output (abridged):

😄  [rutas-norte] minikube v1.33.1 on Ubuntu 24.04
✨  Using the docker driver based on user configuration
👍  Starting "rutas-norte" primary control-plane node in "rutas-norte" cluster
🚜  Pulling base image ...
🔥  Creating docker container (CPUs=4, Memory=8192MB) ...
🐳  Preparing Kubernetes v1.30.0 on Docker 26.1.1 ...
🔎  Verifying Kubernetes components...
🌟  Enabled addons: default-storageclass, storage-provisioner
🏄  Done! kubectl is now configured to use "rutas-norte" cluster and "default" namespace by default

Two important details in that output:

  • The last line says that minikube has modified your kubeconfig and has created and activated a context called rutas-norte. That is what makes kubectl talk to this cluster.
  • Two addons have already been enabled by default: default-storageclass and storage-provisioner. They are the ones that will let us ask for persistent volumes in module 5 without any real infrastructure.

If your machine has fewer resources, start with --cpus=2 --memory=4096 and bear it in mind: in module 7 you will have to switch components off to make room.

  1. Addons required for the course

Addons are pre-packaged components that minikube installs into the cluster. Enable these three right away:

minikube addons enable ingress --profile=rutas-norte
minikube addons enable metrics-server --profile=rutas-norte
minikube addons list --profile=rutas-norte
Addon What it installs What we will need it for
ingress An NGINX-based Ingress Controller Publishing www.rutasnorte.example and api.rutasnorte.example (module 4)
metrics-server A CPU and memory metrics collector kubectl top and horizontal autoscaling (modules 7 and 9)
storage-provisioner A dynamic volume provisioner The bookings-postgres disk (module 5). Already active by default
dashboard (optional) The cluster's web interface Handy for exploring; opened with minikube dashboard

Check that the Ingress controller has started before moving on:

kubectl get pods -n ingress-nginx
NAME                                       READY   STATUS      RESTARTS   AGE
ingress-nginx-admission-create-9k2xq       0/1     Completed   0          62s
ingress-nginx-admission-patch-hn4vp        0/1     Completed   0          62s
ingress-nginx-controller-768f948f8f-7lxjp  1/1     Running     0          62s

The two pods in Completed are not an error: they are installation Jobs that have already finished their work (remember from the previous lesson that a Job finishes, it does not stay running). The one that matters is the third, in Running and 1/1.

  1. Verifying the cluster

Three checks that are always worth doing after creating a cluster.

1. The node is ready:

kubectl get nodes -o wide
NAME          STATUS   ROLES           AGE   VERSION   INTERNAL-IP    OS-IMAGE             CONTAINER-RUNTIME
rutas-norte   Ready    control-plane   3m    v1.30.0   192.168.49.2   Ubuntu 22.04.4 LTS   docker://26.1.1

What matters: STATUS: Ready (the kubelet is reporting and the network plugin works) and ROLES: control-plane (in minikube the same node acts as control plane and as worker, something that would never happen in production).

2. The system components are healthy:

kubectl get pods -A
NAMESPACE       NAME                                  READY   STATUS    RESTARTS   AGE
ingress-nginx   ingress-nginx-controller-768f948f8f   1/1     Running   0          2m
kube-system     coredns-7db6d8ff4d-4rzmt              1/1     Running   0          3m
kube-system     etcd-rutas-norte                      1/1     Running   0          3m
kube-system     kube-apiserver-rutas-norte            1/1     Running   0          3m
kube-system     kube-controller-manager-rutas-norte   1/1     Running   0          3m
kube-system     kube-proxy-9wgbn                      1/1     Running   0          3m
kube-system     kube-scheduler-rutas-norte            1/1     Running   0          3m
kube-system     metrics-server-7d9f8c6b5-x2klp        1/1     Running   0          1m
kube-system     storage-provisioner                   1/1     Running   0          3m

Recognise this list: these are exactly the components from the lesson Kubernetes Architecture, now really running on your machine. Note that in minikube the control plane runs as pods inside the cluster itself (static pods managed by the kubelet), and that coredns appears, the internal DNS server we will study in module 4.

3. The API responds and you know which cluster you are talking to:

kubectl cluster-info
kubectl config current-context
Kubernetes control plane is running at https://192.168.49.2:8443
CoreDNS is running at https://192.168.49.2:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
rutas-norte

If the three checks pass, you have a working cluster.

  1. Lifecycle: stop, resume, destroy

A local cluster consumes resources even when you are not using it. These are the four commands you need:

# Stop the cluster, keeping ALL its state (objects, images, volumes)
minikube stop --profile=rutas-norte

# Resume it exactly as it was, with the same configuration
minikube start --profile=rutas-norte

# See the current state
minikube status --profile=rutas-norte

# Destroy it completely: everything deployed is lost
minikube delete --profile=rutas-norte
Command What it keeps When to use it
stop Everything: objects, volume data, pulled images When you finish a study session
start (on an existing profile) Everything above When you pick the course back up
delete Nothing from that profile When the cluster is broken or when you finish the course

Method tip: not being afraid of minikube delete is precisely the goal. If all your manifests are versioned in the repository's k8s/ directory —as we will do from lesson 01-07 onwards— recreating the cluster and reapplying them should take you five minutes. If deleting it scares you, that is a sign there is important state that is not in Git.

You will also find these useful:

# Open a shell inside the node (to inspect the runtime)
minikube ssh --profile=rutas-norte

# Get the node's IP, which we will need for module 4's Ingress
minikube ip --profile=rutas-norte
192.168.49.2

  1. The kind alternative and a multi-node cluster

kind (Kubernetes IN Docker) creates each node as a Docker container. It is lighter than minikube and its great advantage is how easy it makes building a multi-node cluster, something that will come in handy when we study scheduling and affinity (module 6). Its downside: it ships no addons, so Ingress and storage take extra steps.

Installing it on Linux:

curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.23.0/kind-linux-amd64
sudo install ./kind /usr/local/bin/kind && rm ./kind
kind version

A configuration file with one control node and two workers, also prepared for Ingress:

# k8s/local-environment/kind-rutas-norte.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: rutas-norte
nodes:
  - role: control-plane
    # The label kind's Ingress Controller expects to find
    kubeadmConfigPatches:
      - |
        kind: InitConfiguration
        nodeRegistration:
          kubeletExtraArgs:
            node-labels: "ingress-ready=true"
    # We publish the node's 80 and 443 on the host machine
    extraPortMappings:
      - containerPort: 80
        hostPort: 80
        protocol: TCP
      - containerPort: 443
        hostPort: 443
        protocol: TCP
  - role: worker
    labels:
      zone: north
  - role: worker
    labels:
      zone: south

An explanation of the parts that matter:

  • nodes: each entry is a Docker container that will act as a node. Here we will have three.
  • extraPortMappings: without this, the cluster's port 80 would not be reachable from your browser. It is the equivalent of minikube tunnel.
  • labels on the workers: node labels we will use to practise affinity and anti-affinity in module 6, simulating two availability zones.

Creating and verifying:

kind create cluster --config k8s/local-environment/kind-rutas-norte.yaml
kubectl get nodes
NAME                        STATUS   ROLES           AGE   VERSION
rutas-norte-control-plane   Ready    control-plane   75s   v1.30.0
rutas-norte-worker          Ready    <none>          52s   v1.30.0
rutas-norte-worker2         Ready    <none>          52s   v1.30.0

Installing the Ingress Controller (kind does not ship it) and deleting the cluster when it is no longer needed:

kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
kind delete cluster --name rutas-norte

What to choose: if in doubt, minikube. Use kind when you need several nodes or when you build Kubernetes inside a continuous integration pipeline, where its creation speed is decisive.

  1. Troubleshooting common start-up failures

Symptom Likely cause Fix
Exiting due to RSRC_INSUFFICIENT_CORES You asked for more CPU than is available Lower --cpus, or raise Docker Desktop's resources in Settings → Resources
docker: permission denied while trying to connect to the Docker daemon socket Your user is not in the docker group sudo usermod -aG docker $USER && newgrp docker
Unable to pick a default driver There is no Docker and no hypervisor available Install Docker and start its service; then --driver=docker
The start-up hangs at Pulling base image Slow network, corporate proxy or pull rate limit Set HTTP_PROXY/HTTPS_PROXY, or retry; the base image is around 1 GB
Node permanently NotReady The network plugin is not ready or there is not enough memory kubectl describe node and check the conditions; usually solved with minikube delete and recreating
Pods in ImagePullBackOff Non-existent image, misspelled tag or a private registry with no credentials kubectl describe pod and read the events; check the exact image name
System pods in CrashLoopBackOff after suspending the laptop Clock drift between host and node, which invalidates the certificates minikube stop && minikube start on the same profile
kubectl answers connection refused The cluster is stopped or the context points somewhere else minikube status and kubectl config use-context rutas-norte
The connection to the server localhost:8080 was refused There is no kubeconfig: kubectl falls back to the default Start minikube, or export KUBECONFIG pointing at the right file
You run out of disk halfway through the course Accumulated images minikube ssh -- docker system prune -a, or recreate the cluster with a bigger --disk-size

General-purpose diagnostic commands when something goes wrong at start-up:

minikube status --profile=rutas-norte
minikube logs --profile=rutas-norte | tail -50
kubectl get events -A --sort-by=.metadata.creationTimestamp | tail -20

The third one is especially valuable and we will use it a lot: events are the cluster's diary and they almost always contain a plain-text explanation of why something did not work.

Common Mistakes and Tips

  • Starting with the defaults (2 CPUs, 2 GiB). It works for the first modules and falls short as soon as you add observability. Sizing it properly from the start beats recreating the cluster halfway through the course.
  • Not using --profile. If you work with several local clusters, the default profile becomes a source of confusion: you end up applying manifests to the wrong cluster. Naming the profile rutas-norte always makes it clear where you are.
  • Changing the Kubernetes version between sessions. If one day you start with 1.30 and another without pinning a version, behaviours and APIs change. Pin the version in the command and write it down in the repository.
  • Confusing "the cluster started" with "the cluster is ready". minikube start returns before the addons are operational. Always check with kubectl get pods -A that nothing is in Pending or ContainerCreating.
  • Expecting a Service of type LoadBalancer to get an external IP. There is no cloud-controller-manager locally (we saw this in 01-02): it stays at <pending>. It is solved with minikube tunnel, and we will cover it in module 4.
  • Leaving the cluster running permanently. It eats CPU and battery. minikube stop at the end of the session keeps absolutely everything.
  • Tip: save your cluster creation command in the repository itself (for example, in k8s/local-environment/README or a create-cluster.sh script). It is as much a part of the project's documentation as the manifests are.

Exercises

Exercise 1: Create and verify your cluster

Build the practice cluster with the rutas-norte profile, 4 CPUs, 8 GiB and Kubernetes 1.30, enable the ingress and metrics-server addons, and answer:

  1. How many nodes does it have and what role do they have?
  2. How many pods are there in the kube-system namespace and which of them match the control plane components you studied in lesson 01-02?
  3. What is the node's IP?
  4. Which kubectl context is active?

Exercise 2: Lifecycle and state persistence

Create the project's development namespace, stop the cluster, resume it and check whether the namespace still exists:

kubectl create namespace rutas-norte-dev
minikube stop --profile=rutas-norte
minikube start --profile=rutas-norte
kubectl get namespaces

Explain why the result is what it is, mentioning where that object is stored. Then state which command would make that namespace disappear along with everything else.

Exercise 3: A multi-node cluster with kind

Use kind to create a cluster called rutas-norte-multi with one control node and two workers labelled zone: north and zone: south. Check that the three nodes are Ready and list only the nodes in the north zone. Explain in two lines why this cluster will be useful to you in module 6 and why we do not use it as the course's main environment.

Solutions

Solution 1

minikube start --profile=rutas-norte --driver=docker \
  --kubernetes-version=v1.30.0 --cpus=4 --memory=8192 --disk-size=40g
minikube addons enable ingress --profile=rutas-norte
minikube addons enable metrics-server --profile=rutas-norte
  1. A single node, with the control-plane role. In minikube that same node also runs user workloads, which is not done in production: the control plane is reserved through taints.
  2. Around eight pods. They match the architecture: etcd, kube-apiserver, kube-scheduler, kube-controller-manager (control plane), kube-proxy (node), plus coredns (internal DNS), storage-provisioner and metrics-server. cloud-controller-manager does not appear because there is no cloud provider.
  3. minikube ip --profile=rutas-norte → typically 192.168.49.2.
  4. kubectl config current-contextrutas-norte.

Solution 2

The namespace still exists after stop and start. The reason is that stop destroys nothing: it shuts down the node's container or VM while keeping its disk, and on that disk lives etcd, which is where the Namespace object resides. On resuming, the apiserver reads from that same etcd again and all the declared state reappears.

The command that would destroy everything is minikube delete --profile=rutas-norte, which removes the node and its disk, etcd included. That is why manifests must live in Git: recreating the cluster is then a formality.

Solution 3

kind create cluster --config k8s/local-environment/kind-rutas-norte.yaml --name rutas-norte-multi
kubectl get nodes --show-labels
kubectl get nodes -l zone=north
NAME                              STATUS   ROLES    AGE   VERSION
rutas-norte-multi-worker          Ready    <none>   90s   v1.30.0

It will be useful in module 6 because affinity, anti-affinity, taints and topology spread can only really be practised with several nodes: on a single-node cluster the scheduler has no decision to make. We do not use it as the main environment because kind includes no addons: you would have to install the Ingress Controller and a storage provisioner by hand, work that at this point in the course distracts from the goal.

Conclusion

You now have a Kubernetes cluster running on your machine, with the rutas-norte profile, version 1.30 pinned, enough resources for the whole course and the Ingress, metrics and dynamic storage addons enabled. You have also seen the full landscape of options —local, self-managed with kubeadm, managed in the cloud— and the criteria for choosing one or another in a real project, plus a multi-node alternative with kind for when we get to scheduling. And, above all, you know how to stop it, resume it and destroy it without fear, because the operational truth of this course is that the cluster is replaceable and the manifests are not.

You have the environment, but you have barely used the tool you are going to talk to it with. In the next lesson, The Kubernetes CLI: kubectl, we will master kubectl thoroughly: the kubeconfig file and contexts, the verbs you will use every day, the output formats, selection by labels and the productivity tricks that make the difference between fighting the cluster and working with ease.

Kubernetes Course

Module 1: Introduction to Kubernetes

Module 2: Core Kubernetes Components

Module 3: Configuration and Secret Management

Module 4: Networking in Kubernetes

Module 5: Storage in Kubernetes

Module 6: Advanced Kubernetes Concepts

Module 7: Monitoring and Logging

Module 8: Kubernetes Security

Module 9: Scaling and Performance

Module 10: Kubernetes Ecosystem and Tooling

Module 11: Case Studies and Real-World Applications

Module 12: Preparing for Kubernetes Certification

© Copyright 2026. All rights reserved