Back to Distributed Systems & Kubernetes Series

Minikube Track Part 1: Install & Addons

June 6, 2026 Wasil Zafar 30 min read

Minikube is the fastest way to run a full Kubernetes cluster locally. Install it, pick the right driver for your OS, and use addons to enable ingress, metrics-server, and a local container registry — all without touching a cloud provider.

Table of Contents

  1. Minikube Architecture
  2. Installation
  3. Drivers & Profiles
  4. Essential Addons
  5. Local Image Workflow
  6. Exercises
  7. Key Takeaways

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
kubectl is not included with Minikube. Without it, 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
kubeconfig: Minikube automatically updates your ~/.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
Recommendation: Use 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:

  1. Provision: The driver creates an isolated environment (container or VM) with a known base image containing systemd, containerd, and network tooling.
  2. Bootstrap: Minikube runs kubeadm init inside that environment to create a single-node Kubernetes cluster.
  3. Configure: The kubeconfig on your host is updated with a new context pointing to the cluster's API server.
  4. 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.28 and v1.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
Tip: Use a profile per feature branch when you need to test against different Kubernetes versions or different addon configurations. The -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
Security note: The Minikube dashboard is not secured by default — it grants full cluster admin access to anyone who can reach it. Never expose it outside localhost. For production dashboards, use Headlamp or configure dashboard with RBAC and authentication.

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
Skaffold integration: For an automated build-reload loop with Minikube, use Skaffold (covered in the Skaffold Track). Skaffold handles 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

Exercise 1 — Install & Start: Install Minikube using your OS's package manager. Start a cluster with the docker driver and --kubernetes-version=v1.29.0. Verify with kubectl get nodes and minikube status. Then stop and restart — confirm the cluster state is preserved.
Exercise 2 — Addons: Enable the 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.
Exercise 3 — Local Image: Write a simple Go or Python HTTP server, build it as a Docker image tagged 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

Key Takeaways:
  • Minikube runs Kubernetes in a VM or container on your laptop — choose the docker driver for the fastest start time
  • Profiles isolate multiple clusters: use minikube start -p <name> to create, and minikube 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 set imagePullPolicy: 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.