Working against a remote Kubernetes cluster has a specific cost. Build the image, push it to a registry, deploy, wait. Three minutes per cycle on a good day. When you’re iterating on a network policy, testing resource limits, or debugging a pod that won’t start, that’s not a feedback loop — it’s punishment.
Minikube gives you a real Kubernetes cluster that runs on your machine. Not a simplified version: the same API server, the same scheduler, the same RBAC model. And critically, you can run several named clusters in parallel — one per project, each with its own CPU and memory budget — and switch between them cleanly without tearing anything down.
This post covers installation on Mac, creating multiple profiles with custom resources, switching between them using kubectl contexts, exporting a per-cluster kubeconfig, and using k9s to make day-to-day interaction fast.
Installing Minikube
The cleanest path on Mac is Homebrew:
brew install minikube
Verify the installation:
minikube version
Choosing a driver
Minikube needs a driver — something to run the cluster VM or container. On Mac, two options work well in practice:
Docker — if Docker Desktop is already running, Minikube detects it automatically. No extra setup.
QEMU — lighter than Docker Desktop, runs natively on both Intel and Apple Silicon:
brew install qemu
If you already have Docker Desktop, skip QEMU — Minikube will use it by default. You can also pin the driver explicitly so it’s not re-evaluated on each start:
minikube config set driver docker
# or
minikube config set driver qemu2
Starting a cluster
minikube start
This creates a cluster named minikube — the default profile — with 2 CPUs and 2 GB of RAM. For workloads that actually resemble production:
minikube start --cpus 4 --memory 8192
Memory is in MB. 8192 = 8 GB. Once the cluster is up, verify it:
minikube status
kubectl cluster-info
kubectl get nodes
You should see one node in Ready state.
Multiple clusters with profiles
A profile is an independent Minikube cluster — its own VM or container, its own Kubernetes state, its own network. Profiles run in parallel without interfering with each other. This is the mechanism that lets you maintain one cluster per project and switch between them without teardown.
Creating named clusters
# A resource-intensive microservices project
minikube start -p project-a --cpus 4 --memory 8192
# A lighter stack — a database and a couple of APIs
minikube start -p project-b --cpus 2 --memory 4096
Both clusters run independently. List all of them:
minikube profile list
|-----------|--------|---------|--------------|------|---------|---------|-------|--------|
| Profile | VM | Runtime | IP | Port | Version | Status | Nodes | Active |
|-----------|--------|---------|--------------|------|---------|---------|-------|--------|
| project-a | docker | docker | 192.168.49.2 | 8443 | v1.31.0 | Running | 1 | * |
| project-b | docker | docker | 192.168.49.3 | 8443 | v1.31.0 | Running | 1 | |
|-----------|--------|---------|--------------|------|---------|---------|-------|--------|
The * marks the currently active profile.
Cluster lifecycle
# Stop without losing state
minikube stop -p project-a
# Restart (picks up exactly where it left off)
minikube start -p project-a
# Pause: frees CPU without stopping the cluster
minikube pause -p project-b
# Unpause
minikube unpause -p project-b
# Delete completely
minikube delete -p project-b
Stopping preserves the cluster’s state — workloads, configmaps, secrets. Deleting removes everything.
Switching between clusters
When Minikube creates a profile, it automatically adds a kubectl context with the same name to ~/.kube/config. A context is a named entry — cluster endpoint, credentials, namespace — that tells kubectl which cluster to target.
List and switch
# Show all available contexts
kubectl config get-contexts
# Switch globally
kubectl config use-context project-a
kubectl config use-context project-b
# Confirm the active context
kubectl config current-context
Per-command targeting
Switching the global context affects every terminal window. If you’re working on two projects simultaneously, use the --context flag to target a specific cluster without changing the global pointer:
kubectl get pods --context=project-a
kubectl get services --context=project-b
Per-session kubeconfig
For longer sessions — or for scripts that need to mirror what CI does — export a standalone kubeconfig for a specific cluster and point the terminal at it:
# Extract the kubeconfig for project-a
kubectl config view --minify --context=project-a --raw > ~/.kube/project-a.yaml
# In that terminal session only
export KUBECONFIG=~/.kube/project-a.yaml
kubectl get pods # always hits project-a, regardless of global context
This pattern isolates the session completely. No risk of accidentally deploying to the wrong cluster while a script runs.
k9s
kubectl is precise and exhaustive. Once the cluster is running, k9s is how you actually interact with it: a terminal UI that shows pods, deployments, services, events, and logs in real time. Everything you’d reach for in kubectl, available without remembering the exact flags.
Install
brew install k9s
Connect to a cluster
By default, k9s uses the active kubectl context. To target a specific cluster without changing the global context:
# Use the active context
k9s
# Target a specific context
k9s --context project-a
# Use a standalone kubeconfig file
k9s --kubeconfig ~/.kube/project-a.yaml
Navigate
k9s uses :resource commands to move between resource types. Press : and type the resource name:
:pods— all pods in the current namespace:deploy— deployments:svc— services:ns— namespaces (select to switch):cm— configmaps:secret— secrets
From any resource list, with the cursor on a row:
l— stream logs from the selected pods— exec a shell into the selected podd— describe (equivalent tokubectl describe)e— edit in$EDITORctrl+d— delete the selected resourcectrl+k— force-kill the selected pod/— filter the current listesc— go back / close panelq— quit k9s
The combination of :ns to switch namespace, l for logs, and s for exec covers most of what you’d do in a debugging session.
Quick reference
Cluster lifecycle
minikube startminikube start -p name --cpus N --memory Nminikube profile listminikube stop -p nameminikube start -p nameminikube pause -p nameminikube delete -p nameContext management
kubectl config get-contextskubectl config use-context namekubectl config current-contextkubectl get pods --context=namekubectl config view --minify --context=name --raw > name.yamlexport KUBECONFIG=~/.kube/name.yamlk9s shortcuts
The local cluster is the foundation for everything else in a Kubernetes workflow. Once this is in place, the next natural step is a local image registry — build an image and have it immediately available to Minikube without pushing to Docker Hub. That’s a five-minute setup and it deserves its own post.