Before you deploy anything to a real EKS cluster, you need somewhere cheap, fast, and disposable to experiment — that’s exactly what Minikube is for. I’ve used it for years as a local sandbox to test manifests, try out new CRDs, and break things safely before they ever touch a shared cluster. This guide walks through getting Minikube running, configuring it properly, and using it in a way that actually mirrors production patterns, so what you learn locally transfers directly to EKS.
What Is Minikube?
Minikube runs a single-node (or optionally multi-node) local Kubernetes cluster inside a VM or container on your machine. It’s not a toy — it runs the real kube-apiserver, kube-scheduler, kube-controller-manager, kubelet, and etcd, just packaged for local development. This makes it distinct from tools like kind (which is container-based and more CI-oriented) or fully managed clusters like EKS.
Installing Minikube
On macOS:
brew install minikube
On Linux:
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
On Windows (via Chocolatey):
choco install minikube
You’ll also need kubectl:
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
kubectl version --client
Choosing a Driver
Minikube supports multiple “drivers” — the underlying virtualization or container runtime it uses:
- docker — runs Minikube as a container using Docker; simplest option on most machines
- hyperkit / hyperv / virtualbox — full VM-based isolation
- podman — rootless container alternative to Docker
For most developers today, Docker is the path of least resistance:
minikube start --driver=docker
Set it as the default so you don’t need the flag every time:
minikube config set driver docker
Starting Your Cluster
Basic start:
minikube start
More realistic production-mirroring start, specifying resources and a specific Kubernetes version matching your EKS clusters:
minikube start \
--driver=docker \
--cpus=4 \
--memory=8192 \
--disk-size=40g \
--kubernetes-version=v1.29.0 \
--nodes=2
Matching --kubernetes-version to your EKS cluster’s control plane version is genuinely important — testing manifests against the same API version you’ll deploy to avoids “works locally, breaks in prod” surprises from deprecated or graduated APIs.
Verify it’s running:
minikube status
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured
Confirm kubectl is pointed at it:
kubectl config current-context
kubectl get nodes
NAME STATUS ROLES AGE VERSION
minikube Ready control-plane 1m v1.29.0
minikube-m02 Ready <none> 1m v1.29.0
Enabling Add-ons
Minikube ships with a curated set of add-ons that mirror common cluster services:
minikube addons list
Useful ones for local development:
minikube addons enable ingress
minikube addons enable metrics-server
minikube addons enable dashboard
minikube addons enable storage-provisioner
The ingress add-on installs NGINX Ingress Controller, letting you test Ingress resources locally exactly as you would with the AWS Load Balancer Controller on EKS (the resource spec is the same; only the controller implementation differs).
Launch the dashboard:
minikube dashboard
Deploying a Sample Application
Let’s deploy something real to confirm everything works end-to-end.
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-node
labels:
app: hello-node
spec:
replicas: 2
selector:
matchLabels:
app: hello-node
template:
metadata:
labels:
app: hello-node
spec:
containers:
- name: hello-node
image: registry.k8s.io/e2e-test-images/agnhost:2.39
args: ["netexec", "--http-port=8080"]
ports:
- containerPort: 8080
resources:
requests:
cpu: 100m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
name: hello-node
spec:
selector:
app: hello-node
ports:
- port: 80
targetPort: 8080
type: LoadBalancer
kubectl apply -f hello-node.yaml
kubectl get deployment hello-node
kubectl get pods -l app=hello-node
Minikube can’t actually provision a cloud load balancer, so it provides a tunnel to simulate LoadBalancer services:
minikube service hello-node
This opens the service in your browser, or prints the accessible URL, e.g. http://127.0.0.1:54021.
Using Local Images Without a Registry
One of the most useful things for local dev: building images directly inside Minikube’s Docker daemon so you don’t need to push to ECR just to test.
eval $(minikube docker-env)
docker build -t hello-node:dev .
Then reference it in your manifest with imagePullPolicy: Never so Kubernetes doesn’t try to pull from a registry:
spec:
containers:
- name: hello-node
image: hello-node:dev
imagePullPolicy: Never
Remember to eval $(minikube docker-env) in every new shell session where you want to build into Minikube’s daemon — it’s a per-shell environment change, not persistent.
Persistent Volumes Locally
Minikube’s default storage-provisioner add-on dynamically provisions hostPath-backed PersistentVolumes, letting you test PVC-based workloads (like the StatefulSets or PVC articles in this series) without needing EBS:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: standard
kubectl apply -f test-pvc.yaml
kubectl get pvc
kubectl get pv
Helm on Minikube
Minikube is also the ideal place to test Helm charts before pointing them at EKS:
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install my-release bitnami/nginx --set service.type=NodePort
CI Integration
For local CI runs (e.g., testing a Helm chart or Kustomize overlay before opening a PR), you can start Minikube inside GitHub Actions:
name: local-cluster-test
on: [pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Start Minikube
uses: medyagh/setup-minikube@latest
- name: Deploy and Test
run: |
kubectl apply -f k8s/
kubectl wait --for=condition=available deployment/hello-node --timeout=60s
kubectl get pods
This is genuinely useful for smoke-testing manifests in CI before they ever reach a staging EKS cluster — catching syntax errors, missing resource limits, or bad selectors early and cheaply.
Multi-Node Clusters and Testing Scheduling Behavior
A single-node Minikube cluster is fine for basic manifest testing, but it can’t teach you anything about scheduling behavior — pod anti-affinity, topology spread constraints, node selectors targeting specific labeled nodes. Since Minikube supports multi-node clusters (as shown in the earlier --nodes=2 example), you can actually exercise these patterns locally:
minikube start --nodes=3 --cpus=2 --memory=4096
kubectl label node minikube-m02 workload-type=batch
kubectl label node minikube-m03 workload-type=batch
apiVersion: apps/v1
kind: Deployment
metadata:
name: spread-test
spec:
replicas: 4
selector:
matchLabels:
app: spread-test
template:
metadata:
labels:
app: spread-test
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: spread-test
containers:
- name: app
image: nginx:1.25
kubectl apply -f spread-test.yaml
kubectl get pods -l app=spread-test -o wide
Watching pods actually distribute across minikube, minikube-m02, and minikube-m03 gives you real confidence the constraint works before it matters on a 50-node EKS cluster.
Testing Kubernetes Version Upgrades Locally
Minikube also supports multiple named cluster “profiles,” letting you run several independent clusters side by side — useful for testing an upgrade path before touching a real cluster:
minikube start -p k8s-129 --kubernetes-version=v1.29.0
minikube start -p k8s-130 --kubernetes-version=v1.30.0
kubectl config get-contexts
kubectl config use-context k8s-130
Deploy the same manifests to both profiles and diff kubectl diff output or check for deprecation warnings before scheduling an actual EKS control-plane upgrade — a cheap way to catch API deprecations early.
Simulating Ingress and TLS Locally
Beyond the basic ingress addon, you can test TLS termination behavior that mirrors an ACM-backed ALB in production:
minikube addons enable ingress
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout tls.key -out tls.crt -subj "/CN=app.local"
kubectl create secret tls app-tls --cert=tls.crt --key=tls.key
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts: ["app.local"]
secretName: app-tls
rules:
- host: app.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hello-node
port:
number: 80
Add 127.0.0.1 app.local to /etc/hosts (pointing at minikube ip if using a VM-based driver) and you have a locally working HTTPS ingress flow that exercises the same resource shapes you’ll deploy against the AWS Load Balancer Controller later.
Common Mistakes
- Forgetting
eval $(minikube docker-env)and being confused why your local image build isn’t visible to the cluster (it built into your host Docker daemon, not Minikube’s). - Using a Kubernetes version wildly different from your production EKS cluster, then being surprised when a manifest that worked locally hits deprecated-API errors in prod, or vice versa.
- Under-provisioning
--memoryand--cpus, causing pods to get evicted or stuck pending on a resource-starved single node. - Forgetting
minikube tunnelis required in a separate terminal for LoadBalancer-type services to get a real routable IP on some driver setups. - Leaving Minikube running in the background indefinitely, silently eating your laptop’s battery and RAM —
minikube stopwhen you’re done for the day,minikube deleteto fully reclaim resources.
Cleaning Up
# Pause without deleting (keeps state, frees CPU)
minikube pause
# Stop entirely
minikube stop
# Delete cluster completely
minikube delete
# Delete all profiles/clusters
minikube delete --all
Troubleshooting
# General cluster logs
minikube logs
# SSH into the Minikube VM/container for deep debugging
minikube ssh
# Check add-on status
minikube addons list
# Reset a broken cluster
minikube delete && minikube start
Best Practices
- Pin
--kubernetes-versionto match your target EKS version for realistic testing. - Use namespaces inside Minikube just like production — don’t get lazy and dump everything into
default. - Script your Minikube setup (
start, addon enablement, seed manifests) in aMakefileor shell script so onboarding new team members takes minutes, not an afternoon of tribal knowledge. - Treat Minikube as a first testing gate before staging — not a replacement for a proper staging EKS cluster, since networking, IAM, storage classes, and load balancer behavior will differ meaningfully from a real cloud environment.
Summary
Minikube gives you a real, disposable, single- or multi-node Kubernetes cluster on your laptop, ideal for fast local iteration before anything touches EKS. Match its Kubernetes version to your production clusters, use the docker-env trick to skip registry pushes during development, and layer in add-ons like ingress and metrics-server to mirror production behavior as closely as possible locally. It won’t replace a real staging environment for testing cloud-specific behavior like IAM, EBS, or ALB integration, but for day-to-day manifest and application development, it’s hard to beat.