Notebook / Infrastructure / 002
essay entry no. 002 · May 03, 2026

Working with Kubernetes locally: Minikube, multiple clusters, and k9s

Iterating against a remote Kubernetes cluster means a build-push-deploy cycle on every change. Minikube gives you real clusters on your machine — several of them — where you can test the full deployment path without consequences.

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

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 pod
  • s — exec a shell into the selected pod
  • d — describe (equivalent to kubectl describe)
  • e — edit in $EDITOR
  • ctrl+d — delete the selected resource
  • ctrl+k — force-kill the selected pod
  • / — filter the current list
  • esc — go back / close panel
  • q — 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

minikube Cluster manager
profile flag -p / --profile name
resource flags --cpus N --memory N
driver docker · qemu2
kubectl Context client
config file ~/.kube/config
per-command --context=name
per-session export KUBECONFIG=path
k9s Terminal UI
target context --context name
target kubeconfig --kubeconfig path
navigate :pods · :deploy · :svc

Cluster lifecycle

operation
minikube command
create (defaults)
minikube start
create with profile
minikube start -p name --cpus N --memory N
list all clusters
minikube profile list
stop (keep state)
minikube stop -p name
restart
minikube start -p name
pause (free CPU)
minikube pause -p name
delete completely
minikube delete -p name

Context management

operation
kubectl command
list contexts
kubectl config get-contexts
switch context
kubectl config use-context name
active context
kubectl config current-context
per-command
kubectl get pods --context=name
export kubeconfig
kubectl config view --minify --context=name --raw > name.yaml
per-session
export KUBECONFIG=~/.kube/name.yaml

k9s shortcuts

k9s
:podspod list
:deploydeployments
:svcservices
:nsnamespaces
:cmconfigmaps
:secretsecrets
lstream logs
sexec shell
ddescribe resource
eedit in $EDITOR
ctrl+ddelete resource
ctrl+kforce kill pod
/filter list
escgo back
qquit k9s

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.

VM

V. M. Casale

backend / cloud / things that go bump in the night

I keep an engineering notebook of the small fixes, environment tricks, and infrastructure patterns that quietly make my work-week better.

Read next.