Minikube Architecture
Minikube runs a single-node (or multi-node) Kubernetes cluster inside a VM or container on your local machine. Unlike cloud-managed clusters, it gives you full control over the Kubernetes version, addons, and configuration — making it ideal for learning, development, and testing.
Hypervisor Drivers
Minikube supports multiple backends for running the cluster VM or container. The right choice depends on your OS and whether you already have Docker installed:
- docker — Uses Docker Desktop or Docker Engine as the node. Fastest startup, no separate VM. Recommended default on Linux and macOS (Docker Desktop).
- podman — Like docker but with the rootless Podman engine. Good for Linux systems without Docker.
- hyperkit — Lightweight macOS-native hypervisor (deprecated in favour of
docker; still works on Intel Macs). - virtualbox — Cross-platform VirtualBox VM. Slower to start but provides full VM isolation. Useful when Docker-in-Docker causes issues.
- hyperv — Windows Hyper-V backend. Requires Windows Pro/Enterprise.
- qemu — QEMU/KVM on Linux. Full virtualisation, works without Docker.
- none — Runs Kubernetes directly on the host (Linux only). Fastest but risky for production data.
Cluster Components
A Minikube cluster runs the standard Kubernetes control-plane components — kube-apiserver, etcd, kube-scheduler, kube-controller-manager — plus kubelet and kube-proxy on the single node. Addons add optional capabilities (ingress, monitoring, registry) via pre-packaged manifests that Minikube manages for you.
Installation
macOS (Homebrew)
# Install Minikube via Homebrew
brew install minikube
# Also install kubectl if you don't have it
brew install kubectl
# Verify versions
minikube version
# minikube version: v1.33.1
# commit: ...
kubectl version --client
# Client Version: v1.30.2
Linux (apt / binary)
# Option 1: Download binary directly (recommended — always latest)
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
# Option 2: .deb package (for Debian/Ubuntu)
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube_latest_amd64.deb
sudo dpkg -i minikube_latest_amd64.deb
# Verify
minikube version
# Install kubectl (required — Minikube does NOT bundle it)
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install kubectl /usr/local/bin/kubectl
rm kubectl
# Verify
kubectl version --client
minikube start will print "kubectl not found" and you will be unable to interact with the cluster. Always install kubectl separately before starting your first cluster, or use the built-in proxy: minikube kubectl -- get pods -A.
Windows (Scoop / Chocolatey / Winget)
# Scoop
scoop install minikube
# Chocolatey
choco install minikube
# Winget
winget install Kubernetes.minikube
# Also install kubectl (not bundled with Minikube)
winget install Kubernetes.kubectl
# PowerShell: verify
minikube version
kubectl version --client
First Start
# Start with the docker driver (recommended default)
minikube start --driver=docker
# Running as root? Docker driver requires --force
# (common in CI containers and cloud dev environments)
minikube start --driver=docker --force
# Start with a specific Kubernetes version
minikube start --driver=docker --kubernetes-version=v1.30.2
# Start with more CPU/memory for resource-heavy workloads
minikube start --driver=docker --cpus=4 --memory=8192
# Check cluster status
minikube status
# minikube
# type: Control Plane
# host: Running
# kubelet: Running
# apiserver: Running
# kubeconfig: Configured
# Verify nodes (requires kubectl installed separately)
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# minikube Ready control-plane 60s v1.30.2
# SSH into the node (for debugging)
minikube ssh
# Stop the cluster (preserves state)
minikube stop
# Delete the cluster entirely
minikube delete
~/.kube/config to add a context named minikube and sets it as the current context. Run kubectl config current-context to verify, and kubectl config use-context minikube to switch back if needed.
Drivers & Profiles
Minikube needs a driver to create the isolated environment where Kubernetes runs. The driver determines how the cluster node is virtualised — as a container, a lightweight VM, or directly on the host. Choosing the right driver affects start-up speed, resource overhead, and compatibility with your OS.
Choosing a Driver
Each driver has trade-offs. Here is a comparison of the most common options:
| Driver | Runs As | OS Support | Best For | Caveats |
|---|---|---|---|---|
| docker | Container | macOS, Linux, Windows | Day-to-day dev (fastest start, lowest overhead) | Cannot run as root without --force; networking is containerised |
| podman | Container (rootless) | Linux | Rootless development; no Docker daemon required | Requires Podman ≥3.0; slightly less battle-tested |
| virtualbox | Full VM | macOS, Linux, Windows | Most isolated; works on older hardware without nested virt | Slowest start (~60s); heavier resource use; incompatible with Hyper-V |
| hyperkit | Lightweight VM | macOS (Intel only) | macOS without Docker Desktop | Deprecated on Apple Silicon; replaced by docker driver |
| kvm2 | Lightweight VM | Linux | Full kernel isolation on Linux without Docker | Requires KVM kernel modules and libvirt |
| hyperv | Hyper-V VM | Windows (Pro/Enterprise) | Windows native VM; no Docker Desktop needed | Requires Hyper-V enabled; Admin privileges |
| none | Host directly | Linux only | CI pipelines (no nested virt); bare-metal testing | Modifies host system; must run as root; no isolation |
docker unless you have a specific reason not to. It is the default, starts in 10–20 seconds, and works identically across macOS, Linux, and Windows. If you are in a CI container already running as root, add --force or switch to the none driver.
# Set the default driver globally (persisted in ~/.minikube/config/config.json)
minikube config set driver docker
# Override per-start without changing the default
minikube start --driver=virtualbox
# Docker driver: check Docker is running
docker info
# VirtualBox driver: check version
vboxmanage --version
# Podman driver (Linux rootless)
minikube start --driver=podman --container-runtime=containerd
# None driver (CI — runs directly on host, must be root)
sudo minikube start --driver=none
How Drivers Work Internally
Regardless of which driver you choose, Minikube follows the same workflow:
- Provision: The driver creates an isolated environment (container or VM) with a known base image containing
systemd,containerd, and network tooling. - Bootstrap: Minikube runs
kubeadm initinside that environment to create a single-node Kubernetes cluster. - Configure: The kubeconfig on your host is updated with a new context pointing to the cluster's API server.
- Addons: Any previously-enabled addons are deployed as Kubernetes resources inside the cluster.
The driver only affects step 1 — everything else is identical. This means your kubectl commands, Helm charts, and manifests work the same way regardless of the underlying virtualisation.
Multiple Profiles
A profile is an independently-configured Minikube cluster with its own driver, Kubernetes version, resource allocation, addons, and kubeconfig context. Profiles let you run multiple isolated clusters simultaneously — useful for:
- Testing the same app against different K8s versions (e.g.,
v1.28andv1.30) - Isolating feature branches that need conflicting addon configs (different ingress controllers, different CNIs)
- Running a "shared services" cluster (PostgreSQL, Redis) alongside an "app" cluster
- Simulating multi-cluster scenarios before moving to Kind or real infrastructure
Each profile stores its state in ~/.minikube/profiles/<name>/ and creates a separate Docker container (or VM). Profiles are completely independent — deleting one does not affect others.
# Start a named profile (creates context 'myapp' in kubeconfig)
minikube start -p myapp --driver=docker --kubernetes-version=v1.30.2
# Start a second profile for a different project
minikube start -p legacy --driver=docker --kubernetes-version=v1.26.0
# List all profiles
minikube profile list
# |---------|-----------|---------|--------------|------|---------|---------|-------|--------|
# | Profile | VM Driver | Runtime | IP | Port | Version | Status | Nodes | Active |
# |---------|-----------|---------|--------------|------|---------|---------|-------|--------|
# | myapp | docker | docker | 192.168.49.2 | 8443 | v1.30.2 | Running | 1 | * |
# | legacy | docker | docker | 192.168.49.3 | 8443 | v1.26.0 | Running | 1 | |
# Switch active profile (also switches kubectl context)
minikube profile myapp
kubectl config current-context # → myapp
# Run any minikube command against a specific profile
minikube status -p legacy
minikube stop -p legacy
minikube delete -p legacy
# Delete all profiles at once
minikube delete --all
-p flag works on all minikube subcommands. Your shell PS1/prompt can show the current minikube profile using $(minikube profile).
Essential Addons
Addons are pre-packaged Kubernetes manifests that Minikube deploys into the cluster automatically. They integrate with Minikube's lifecycle — enable an addon, and it restarts across minikube stop/start cycles.
# List all available addons and their enabled/disabled state
minikube addons list
# Sample output (truncated):
# |-----------------------------|----------|--------------|--------------------------------|
# | ADDON NAME | PROFILE | STATUS | MAINTAINER |
# |-----------------------------|----------|--------------|--------------------------------|
# | dashboard | minikube | disabled | Kubernetes |
# | ingress | minikube | disabled | Kubernetes |
# | ingress-dns | minikube | disabled | minikube |
# | metrics-server | minikube | disabled | Kubernetes |
# | registry | minikube | disabled | Google |
# | storage-provisioner | minikube | enabled ✅ | minikube |
# |-----------------------------|----------|--------------|--------------------------------|
Ingress
# Enable the nginx-ingress controller
minikube addons enable ingress
# Verify the ingress controller pod is running
kubectl get pods -n ingress-nginx
# NAME READY STATUS RESTARTS AGE
# ingress-nginx-admission-create-xxxxx 0/1 Completed 0 60s
# ingress-nginx-controller-7799c6795f-xxxxx 1/1 Running 0 60s
# Get the Minikube IP (use this as the ingress host)
minikube ip
# 192.168.49.2
# Create a test Ingress (maps myapp.local → service myapp-svc:80)
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: myapp.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-svc
port:
number: 80
EOF
# Add to /etc/hosts (or C:\Windows\System32\drivers\etc\hosts on Windows):
# 192.168.49.2 myapp.local
echo "$(minikube ip) myapp.local" | sudo tee -a /etc/hosts
# Test
curl http://myapp.local
Metrics-Server
# Enable metrics-server (required for kubectl top and HPA)
minikube addons enable metrics-server
# Wait ~60s for metrics to be scraped, then:
kubectl top nodes
# NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
# minikube 265m 6% 1102Mi 13%
kubectl top pods -A
# NAMESPACE NAME CPU(cores) MEMORY(bytes)
# kube-system coredns-787d4945fb-xxxxx 3m 14Mi
# kube-system etcd-minikube 22m 47Mi
# HPA now works — autoscale a deployment when CPU > 50%
kubectl autoscale deployment myapp --cpu-percent=50 --min=1 --max=5
kubectl get hpa
Local Registry
# Enable the in-cluster container registry
minikube addons enable registry
# The registry is exposed at 'localhost:5000' inside the cluster
# and at the node IP with a forwarded port externally
# Get the registry NodePort
kubectl get svc -n kube-system registry
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# registry NodePort 10.96.0.1 <none> 80:NNNNN/TCP 60s
# Forward the registry to localhost:5000 for local docker push
kubectl port-forward --namespace kube-system \
$(kubectl get pod -n kube-system -l actual-registry=true -o name) \
5000:5000 &
# Tag and push a local image
docker build -t myapp:latest .
docker tag myapp:latest localhost:5000/myapp:latest
docker push localhost:5000/myapp:latest
# Use in a Deployment (no imagePullPolicy needed — it's in-cluster)
kubectl set image deployment/myapp myapp=localhost:5000/myapp:latest
Dashboard
# Enable the Kubernetes dashboard
minikube addons enable dashboard
# Open in browser automatically (launches a proxy + opens your browser)
minikube dashboard
# Or get the URL without opening the browser
minikube dashboard --url
# http://127.0.0.1:PORT/api/v1/namespaces/kubernetes-dashboard/services/...
# Dashboard runs in the kubernetes-dashboard namespace
kubectl get pods -n kubernetes-dashboard
# NAME READY STATUS RESTARTS AGE
# dashboard-metrics-scraper-xxxxx 1/1 Running 0 2m
# kubernetes-dashboard-xxxxx 1/1 Running 0 2m
Local Image Workflow
When developing locally, you want to test new container images without pushing them to a registry. Minikube provides two approaches: using the Minikube Docker daemon directly, or using minikube image load.
# ── APPROACH 1: Use Minikube's Docker daemon ─────────────────────
# Point your shell's docker CLI at Minikube's daemon
eval $(minikube docker-env)
# Now 'docker build' builds directly into Minikube's image cache
docker build -t myapp:dev .
# Your Deployment can reference it immediately — but you MUST set
# imagePullPolicy: Never, or Kubernetes will try to pull from a registry
kubectl set image deployment/myapp myapp=myapp:dev
kubectl patch deployment myapp -p \
'{"spec":{"template":{"spec":{"containers":[{"name":"myapp","imagePullPolicy":"Never"}]}}}}'
# Undo: revert to your host Docker daemon
eval $(minikube docker-env --unset)
# ── APPROACH 2: minikube image load (docker-env alternative) ─────
# Build with your regular host Docker daemon
docker build -t myapp:dev .
# Load the image directly into Minikube's container runtime
minikube image load myapp:dev
# Verify the image is present in Minikube
minikube image ls | grep myapp
# docker.io/library/myapp:dev
# Use in Deployment with imagePullPolicy: Never
kubectl create deployment myapp --image=myapp:dev
kubectl patch deployment myapp -p \
'{"spec":{"template":{"spec":{"containers":[{"name":"myapp","imagePullPolicy":"Never"}]}}}}'
# Deployment manifest with imagePullPolicy: Never for local development
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 1
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:dev # local image tag
imagePullPolicy: Never # REQUIRED for local images
ports:
- containerPort: 8080
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
eval $(minikube docker-env) automatically and reloads changed files into running containers via file sync — giving you a hot-reload experience for Kubernetes development.
Exercises
--kubernetes-version=v1.29.0. Verify with kubectl get nodes and minikube status. Then stop and restart — confirm the cluster state is preserved.
ingress and metrics-server addons. Deploy the official nginxdemo/hello image as a Deployment with a NodePort Service. Create an Ingress resource pointing to it. Add the Minikube IP to /etc/hosts and access the app via the hostname. Then run kubectl top pods to verify metrics are working.
myserver:local. Use eval $(minikube docker-env) to build it directly into Minikube. Deploy it with imagePullPolicy: Never. Verify the pod starts successfully with kubectl describe pod. Then make a code change, rebuild, and use kubectl rollout restart to pick up the new image.
Key Takeaways
- Minikube runs Kubernetes in a VM or container on your laptop — choose the
dockerdriver for the fastest start time - Profiles isolate multiple clusters: use
minikube start -p <name>to create, andminikube profile <name>to switch - Addons (
ingress,metrics-server,registry,dashboard) are enabled with a single command and persist across restarts - For local images, use
eval $(minikube docker-env)to build directly into Minikube, then setimagePullPolicy: Never minikube tunnel(covered in Part 2) is needed to test LoadBalancer services locally- Use
minikube service <svc-name>to open a NodePort or LoadBalancer service in your browser with a single command
Next in This Track
In Part 2: Multi-Node & Profiles, we simulate multi-node clusters to test PodAffinity, DaemonSets, and workload scheduling — and use profiles to maintain multiple isolated environments per project.