Home Java Spring Boot on Minikube: Local Kubernetes Development Guide
Advanced 5 min · July 14, 2026

Spring Boot on Minikube: Local Kubernetes Development Guide

Learn to run Spring Boot apps on Minikube for local Kubernetes development.

N
Naren Founder & Principal Engineer

20+ years shipping production Java in banking & fintech. Everything here is grounded in real deployments.

Follow
Production
production tested
July 15, 2026
last updated
2,398
articles · all by Naren
Before you start⏱ 20-25 min read
  • Java 17+ and Maven 3.9+ installed
  • Docker Desktop 24+ or equivalent
  • Minikube v1.32+ and kubectl installed
  • Basic Kubernetes knowledge (pods, services, deployments)
  • Spring Boot 3.2.x project with a REST API
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer

• Set up Minikube locally with Spring Boot 3.2.x • Dockerize your app with multi-stage builds for efficiency • Deploy to Minikube using kubectl and YAML manifests • Debug with port-forwarding and live logs • Use Minikube addons like ingress and dashboard for full local dev experience

✦ Definition~90s read
What is Running Spring Boot Applications with Minikube?

Minikube is a tool that runs a single-node Kubernetes cluster locally, enabling you to develop and test containerized Spring Boot applications in a realistic Kubernetes environment without cloud costs.

Think of Minikube as a mini Kubernetes cluster that runs on your laptop, like a model train set for testing real-world railway operations.
Plain-English First

Think of Minikube as a mini Kubernetes cluster that runs on your laptop, like a model train set for testing real-world railway operations. Spring Boot is the engine, and Minikube is the track—you build and test locally before going to production.

Running Spring Boot applications in Kubernetes is the gold standard for modern microservices deployment. But developing locally against a real cluster has historically been painful—you either pay for cloud resources or deal with heavyweight local setups. Minikube changes that. It gives you a single-node Kubernetes cluster that runs in a VM or container on your machine, perfect for local development and testing. In this guide, we'll walk through deploying a Spring Boot 3.2.x application to Minikube. You'll learn how to create a Docker image, write Kubernetes manifests, and debug issues that pop up when your app runs in a containerized environment. This isn't just a tutorial—it's a collection of hard-won lessons from running Spring Boot in production clusters. We'll cover common pitfalls like container image sizing, health checks, and configuration management. By the end, you'll have a repeatable workflow for local Kubernetes development that mirrors production behavior. This is the setup I've used at three different SaaS companies, and it's saved countless hours of 'works on my machine' debugging. Let's get into it.

Setting Up Your Spring Boot Project for Minikube

Before we touch Minikube, your Spring Boot app needs to be container-ready. I'm assuming you have a standard Spring Boot 3.2.x application with a REST controller—something like a simple payment endpoint. The first step is adding the Spring Boot Maven plugin for building an executable JAR. Use Java 17 or 21 for compatibility. Your pom.xml should have the spring-boot-maven-plugin configured with a finalName to avoid version suffixes. Make sure your application.properties doesn't hardcode ports—use server.port=${PORT:8080} so Kubernetes can override it. Also, add management endpoints for health checks: spring-boot-starter-actuator. This is non-negotiable for Kubernetes liveness and readiness probes. I've seen too many apps fail because they didn't expose /actuator/health. Here's the minimal setup:

pom.xml (excerpt)XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.5</version>
</parent>
<properties>
    <java.version>17</java.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <finalName>payment-service</finalName>
            </configuration>
        </plugin>
    </plugins>
</build>
Output
Builds a JAR named payment-service.jar in target/
⚠ Don't Skip Actuator
📊 Production Insight
In production, I also enable /actuator/info with Git commit details and /actuator/metrics for Prometheus. This makes debugging much faster.
🎯 Key Takeaway
Always include Spring Boot Actuator and configure it for Kubernetes health checks before containerizing.

What the Official Docs Won't Tell You

The official Spring Boot Kubernetes guide assumes you have a production cluster ready. They don't tell you that Minikube has quirks that will bite you. First, Minikube's default Docker daemon is separate from your host's Docker. If you build images with your local Docker, Minikube can't see them unless you run 'eval $(minikube docker-env)' or push to a registry. Second, resource limits matter locally—Minikube runs on your machine, so a Spring Boot app with default heap settings (like 2GB) can starve your laptop. Set -Xmx256m in your Dockerfile. Third, Minikube's storage class is 'standard' but slow. If your app uses JPA with Hibernate, initial schema creation can timeout. I once spent three hours debugging 'PodInitializing' because Hibernate took 90 seconds to validate 50 tables. The fix: add 'spring.jpa.properties.hibernate.boot.allow_jdbc_metadata_access=false' and set 'spring.jpa.hibernate.ddl-auto=validate' instead of 'create'. Finally, Minikube's default service type is ClusterIP—you can't access it from your browser without port-forwarding. Don't be that person who changes it to LoadBalancer and wonders why it doesn't work.

DockerfileDOCKERFILE
1
2
3
4
5
6
FROM eclipse-temurin:17-jre-alpine AS runtime
WORKDIR /app
COPY target/payment-service.jar app.jar
EXPOSE 8080
ENV JAVA_OPTS="-Xmx256m -Xms128m"
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
Output
Creates a ~180MB image with constrained heap (256MB max)
🔥Minikube Docker Env
📊 Production Insight
In production, we use Jib or Buildpacks to avoid Dockerfiles entirely. But for Minikube, a simple Dockerfile gives you control over layers.
🎯 Key Takeaway
Set JVM heap limits in Dockerfile and use Minikube's Docker daemon for local image builds.

Dockerizing Your Spring Boot App for Minikube

Now let's build and containerize. First, ensure Minikube is running: 'minikube start --cpus=4 --memory=8192'. I recommend 4 CPUs and 8GB RAM for a decent local experience. Then point your shell to Minikube's Docker: 'eval $(minikube docker-env)'. Build your Spring Boot JAR with Maven: 'mvn clean package -DskipTests'. Then build the Docker image: 'docker build -t payment-service:v1 .'. Verify it's available: 'docker images | grep payment-service'. If you see it, Minikube can use it. Important: Never use the 'latest' tag locally—it causes confusion when you rebuild. Use semantic versions. Also, if your app uses a database (like MySQL via Testcontainers in dev), you'll need to run it as a Minikube pod or service. For simplicity, I'll assume an in-memory H2 database for this example. The Dockerfile above uses a multi-stage build with a JRE base image. This is critical: the full JDK is 400MB+, while the JRE is ~180MB. In a microservice setup, those 200MB differences add up across 50 services. I've seen teams run out of disk on Minikube because they used JDK images. Don't be that team.

Terminal commandsBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Start Minikube with adequate resources
minikube start --cpus=4 --memory=8192

# Point to Minikube's Docker daemon
eval $(minikube docker-env)

# Build the Spring Boot JAR
mvn clean package -DskipTests

# Build Docker image inside Minikube's context
docker build -t payment-service:v1 .

# Verify image exists
docker images | grep payment-service
Output
payment-service v1 a1b2c3d4e5f6 2 minutes ago 180MB
⚠ Don't Use latest Tag
📊 Production Insight
In CI/CD, we push images to a registry (ECR, GCR) and use 'imagePullPolicy: Always'. But for Minikube, use 'imagePullPolicy: IfNotPresent' to avoid pulling from a registry.
🎯 Key Takeaway
Build Docker images inside Minikube's Docker environment and tag them with version numbers.

Writing Kubernetes Manifests for Spring Boot

With the image ready, we need Kubernetes manifests. Create a 'k8s' directory in your project root. Start with a Deployment YAML. Key fields: replicas (1 is fine for local), selector matchLabels, template metadata labels, and container spec. Set resource requests and limits—this is where most local dev setups fail. If you don't set limits, Minikube will let your pod consume all CPU, choking other services. For a Spring Boot app with -Xmx256m, set memory limit to 512MB (leave headroom). CPU limit at 500m (half a core). Add liveness and readiness probes using the actuator endpoint. The readiness probe is especially important—without it, Kubernetes sends traffic to your pod before Spring Boot has finished initializing. I've debugged 'Connection Refused' errors for hours because of this. Also, set 'terminationGracePeriodSeconds: 30' to give Spring Boot time to shut down gracefully. Then a Service manifest to expose the app internally. Use ClusterIP type—we'll access it via port-forwarding.

k8s/deployment.yamlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-service
spec:
  replicas: 1
  selector:
    matchLabels:
      app: payment-service
  template:
    metadata:
      labels:
        app: payment-service
    spec:
      terminationGracePeriodSeconds: 30
      containers:
      - name: payment-service
        image: payment-service:v1
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /actuator/health/liveness
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /actuator/health/readiness
            port: 8080
          initialDelaySeconds: 20
          periodSeconds: 5
Output
Creates a deployment with health probes and resource constraints
🔥Probe Paths in Spring Boot 3.x
📊 Production Insight
In production, I add startupProbe for apps with slow initialization (like those using Flyway migrations). This prevents premature restart loops.
🎯 Key Takeaway
Always configure liveness and readiness probes using Spring Boot Actuator endpoints, and set resource limits.

Deploying to Minikube and Debugging

Deploy your manifests: 'kubectl apply -f k8s/'. Check pod status: 'kubectl get pods'. If it shows 'Running', great. If 'CrashLoopBackOff' or 'Pending', debug immediately. Common issues: image not found (run 'eval $(minikube docker-env)' and rebuild), resource limits too low (check 'kubectl describe pod'), or health probe failing (check logs with 'kubectl logs <pod-name>'). For the first time, watch logs in real-time: 'kubectl logs -f deployment/payment-service'. You should see Spring Boot starting. If it hangs, check if Hibernate is timing out (see Section 2). Once running, access the app via port-forwarding: 'kubectl port-forward service/payment-service 8080:8080'. Then curl http://localhost:8080/actuator/health. You should get a 200 OK. If you get a 404, your actuator endpoints might be misconfigured. Add 'management.endpoints.web.exposure.include=health,info' to application.properties. Also, enable the Minikube dashboard for a visual overview: 'minikube dashboard'. This opens a browser UI showing pods, services, and events. I use it constantly to spot resource issues.

Terminal commandsBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Apply manifests
kubectl apply -f k8s/

# Check pod status
kubectl get pods

# Watch logs
kubectl logs -f deployment/payment-service

# Port-forward to access locally
kubectl port-forward service/payment-service 8080:8080

# Test health endpoint
curl http://localhost:8080/actuator/health

# Open Minikube dashboard
minikube dashboard
Output
{"status":"UP","components":{"livenessState":{"status":"UP"},"readinessState":{"status":"UP"}}}
⚠ Port-Forwarding Is Not Production
📊 Production Insight
In staging, I use Telepresence to intercept traffic from the cluster to my local machine. It's a game-changer for debugging service-to-service calls.
🎯 Key Takeaway
Use kubectl logs and port-forwarding to debug deployments, and enable the Minikube dashboard for visual monitoring.

Configuration Management with ConfigMaps and Secrets

Hardcoding configuration in application.properties is a recipe for disaster. In Kubernetes, use ConfigMaps for non-sensitive config and Secrets for sensitive data. For Spring Boot, you can mount these as files or inject them as environment variables. I prefer environment variables because they're easier to override. Create a ConfigMap for your app properties: 'kubectl create configmap payment-config --from-literal=spring.datasource.url=jdbc:h2:mem:testdb'. For secrets (like API keys), use: 'kubectl create secret generic payment-secrets --from-literal=api-key=supersecret'. Then reference them in your Deployment YAML under 'env'. Spring Boot automatically maps environment variables to properties (e.g., SPRING_DATASOURCE_URL becomes spring.datasource.url). This is a lifesaver. Also, consider using Spring Cloud Kubernetes for dynamic config reload, but that's overkill for Minikube. Keep it simple: mount ConfigMaps as env vars. One gotcha: if you change a ConfigMap, pods won't automatically reload. You need to restart the deployment: 'kubectl rollout restart deployment/payment-service'. I've seen teams waste hours wondering why config changes didn't take effect. Don't be that team.

k8s/deployment-env.yaml (excerpt)YAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
spec:
  template:
    spec:
      containers:
      - name: payment-service
        env:
        - name: SPRING_DATASOURCE_URL
          valueFrom:
            configMapKeyRef:
              name: payment-config
              key: spring.datasource.url
        - name: API_KEY
          valueFrom:
            secretKeyRef:
              name: payment-secrets
              key: api-key
        # Also set via envFrom for bulk injection
        envFrom:
        - configMapRef:
            name: payment-config
        - secretRef:
            name: payment-secrets
Output
Injects config and secrets as environment variables into the Spring Boot container
🔥Spring Boot Environment Variable Mapping
📊 Production Insight
In production, we use External Secrets Operator to sync secrets from AWS Secrets Manager to Kubernetes. For Minikube, plain kubectl create secret is fine.
🎯 Key Takeaway
Use ConfigMaps and Secrets for configuration, and inject them as environment variables. Restart deployments after config changes.

Using Minikube Addons for a Better Dev Experience

Minikube comes with addons that make local development feel more like a real cluster. Enable the ones you need: 'minikube addons enable ingress' for HTTP routing, 'minikube addons enable metrics-server' for resource monitoring, and 'minikube addons enable dashboard' (already covered). The ingress addon is especially useful if you want to test with a real domain locally. Create an Ingress resource that routes to your service. You'll need to add an entry to your /etc/hosts file pointing to Minikube's IP (get it with 'minikube ip'). For example: '192.168.49.2 payment.local'. Then curl http://payment.local/actuator/health. This simulates production traffic patterns. Another addon: 'ingress-dns' for automatic DNS resolution on macOS/Linux. Also enable 'registry' addon if you want a local Docker registry. But for most local dev, the basic addons are enough. One pro tip: if you're using a database like PostgreSQL, deploy it as a StatefulSet with a PersistentVolumeClaim. Minikube supports hostPath volumes, so data persists across pod restarts. I've used this pattern for local development of a billing system—it's far better than an in-memory database.

k8s/ingress.yamlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: payment-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: payment.local
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: payment-service
            port:
              number: 8080
Output
Routes traffic from payment.local to the payment-service
🔥Minikube IP Changes
📊 Production Insight
For production, we use an actual load balancer (AWS ALB) with cert-manager for TLS. Minikube's ingress addon is great for testing routing logic.
🎯 Key Takeaway
Enable Minikube addons like ingress and metrics-server to simulate production-like environments locally.

Troubleshooting Common Minikube Issues

Even with the best setup, things go wrong. Here's my battle-tested troubleshooting guide. Issue 1: 'Pod stuck in Pending'. This usually means insufficient resources. Run 'kubectl describe pod <name>' and check Events. If it says '0/1 nodes are available: insufficient cpu', scale down or increase Minikube resources. You can't change resources after start—delete and recreate with 'minikube delete && minikube start --cpus=8 --memory=12288'. Issue 2: 'ImagePullBackOff'. This means Kubernetes can't find the image. Did you run 'eval $(minikube docker-env)' before building? Check 'docker images' inside Minikube's context. If the image is missing, rebuild. Issue 3: 'CrashLoopBackOff'. The app is starting but crashing. Get logs: 'kubectl logs <pod-name> --previous' to see the last crash. Common causes: missing env vars, port conflicts, or Hibernate connection issues. Issue 4: 'Connection refused' when port-forwarding. Your app might not be ready yet. Wait for the pod to show 'Running' and check logs. Also verify the port in your container spec matches your app's port. Issue 5: Minikube itself crashes. This happens when your laptop runs out of memory. Monitor with 'minikube status' and 'docker stats'. I've had to 'minikube delete' and start fresh more times than I care to admit.

Troubleshooting commandsBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Check pod events
kubectl describe pod <pod-name>

# See last crash logs
kubectl logs <pod-name> --previous

# Check Minikube status
minikube status

# Delete and restart Minikube
minikube delete
minikube start --cpus=8 --memory=12288

# Verify Docker images in Minikube
minikube ssh -- docker images | grep payment-service
Output
Shows pod events, logs, and Minikube health
⚠ Minikube Deletion Wipes Everything
📊 Production Insight
In production, we use tools like Stern for multi-pod log streaming and K9s for terminal-based cluster management. These are overkill for Minikube but worth knowing.
🎯 Key Takeaway
Master the kubectl describe, logs, and minikube status commands. Know when to restart Minikube entirely.
● Production incidentPOST-MORTEMseverity: high

The Case of the Vanishing Pod: Image Pull Backoff in Staging

Symptom
Pods stayed in 'ImagePullBackOff' status after deployment, with no clear error in logs.
Assumption
The team assumed the image was built correctly since it ran fine locally with Docker Compose.
Root cause
The image was 1.2GB due to a bloated base image (openjdk:17-jdk-slim instead of a distroless or Alpine variant) and included unnecessary Maven dependencies.
Fix
Switched to eclipse-temurin:17-jre-alpine for the runtime stage and used multi-stage builds to exclude build tools. Image size dropped to 180MB.
Key lesson
  • Always use multi-stage Docker builds for Spring Boot apps.
  • Monitor image sizes—anything over 500MB is a red flag for microservices.
  • Test deployments locally with Minikube before pushing to staging.
Production debug guideStep-by-step commands for common failures4 entries
Symptom · 01
Pod stuck in 'Pending'
Fix
Run 'kubectl describe pod <name>' and check Events for resource constraints. If insufficient CPU/memory, delete and recreate Minikube with more resources.
Symptom · 02
Pod in 'CrashLoopBackOff'
Fix
Check logs with 'kubectl logs <pod-name> --previous'. Look for OOMKilled (increase memory limits), missing env vars (check ConfigMaps), or port conflicts.
Symptom · 03
Application starts but health check fails
Fix
Verify actuator endpoints are enabled. Run 'kubectl exec -it <pod-name> -- curl localhost:8080/actuator/health'. If not accessible, check application.properties.
Symptom · 04
Cannot access app via port-forwarding
Fix
Ensure pod is 'Running'. Check 'kubectl get endpoints' to verify service has an endpoint. Verify port numbers match between container spec and service.
★ Spring Boot on Minikube: Quick Debug Cheat SheetFive-minute fixes for common issues
ImagePullBackOff
Immediate action
Rebuild image inside Minikube's Docker context
Commands
eval $(minikube docker-env) && docker build -t payment-service:v1 .
kubectl rollout restart deployment/payment-service
Fix now
Check image exists: 'minikube ssh -- docker images'
Pod stuck Pending (resources)+
Immediate action
Check node resources and delete if necessary
Commands
kubectl describe node | grep -A5 'Capacity'
minikube delete && minikube start --cpus=8 --memory=12288
Fix now
Reduce deployment resource requests temporarily
Connection refused on port-forward+
Immediate action
Check if app is actually listening on the port
Commands
kubectl exec -it <pod-name> -- wget -O- http://localhost:8080/actuator/health
kubectl logs deployment/payment-service | tail -20
Fix now
Verify server.port=8080 in application.properties
Config changes not taking effect+
Immediate action
Restart the deployment
Commands
kubectl rollout restart deployment/payment-service
kubectl rollout status deployment/payment-service
Fix now
Verify ConfigMap exists: 'kubectl get configmap payment-config -o yaml'
FeatureDocker ComposeMinikube
Kubernetes API compatibilityNoneFull
Health probe supportManualBuilt-in liveness/readiness
Resource limits enforcementBasicFull Kubernetes enforcement
Service discoveryDocker DNSKubernetes DNS
Local development speedFastSlower (VM overhead)
Production parityLowHigh
⚙ Quick Reference
8 commands from this guide
FileCommand / CodePurpose
pom.xml (excerpt)Setting Up Your Spring Boot Project for Minikube
DockerfileFROM eclipse-temurin:17-jre-alpine AS runtimeWhat the Official Docs Won't Tell You
Terminal commandsminikube start --cpus=4 --memory=8192Dockerizing Your Spring Boot App for Minikube
k8sdeployment.yamlapiVersion: apps/v1Writing Kubernetes Manifests for Spring Boot
Terminal commandskubectl apply -f k8s/Deploying to Minikube and Debugging
k8sdeployment-env.yaml (excerpt)spec:Configuration Management with ConfigMaps and Secrets
k8singress.yamlapiVersion: networking.k8s.io/v1Using Minikube Addons for a Better Dev Experience
Troubleshooting commandskubectl describe pod Troubleshooting Common Minikube Issues

Key takeaways

1
Set up Minikube with adequate resources and use its Docker daemon for local image builds.
2
Always include Spring Boot Actuator health probes and configure resource limits in Kubernetes manifests.
3
Use ConfigMaps and Secrets for configuration management, and restart deployments after changes.
4
Leverage Minikube addons like ingress and dashboard for a more production-like local environment.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
How would you configure health probes for a Spring Boot application in K...
Q02SENIOR
Explain how to handle configuration for a Spring Boot app across differe...
Q03SENIOR
What are common reasons a Spring Boot pod crashes on Minikube, and how d...
Q01 of 03SENIOR

How would you configure health probes for a Spring Boot application in Kubernetes?

ANSWER
I'd use Spring Boot Actuator with liveness and readiness endpoints. The liveness probe checks if the app is alive (e.g., /actuator/health/liveness), and the readiness probe checks if it's ready to accept traffic (e.g., /actuator/health/readiness). In the deployment YAML, I set initialDelaySeconds to account for startup time, and periodSeconds for frequency.
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
Why use Minikube instead of Docker Compose for Spring Boot development?
02
How do I access my Spring Boot app running on Minikube from my browser?
03
Can I use a database with Spring Boot on Minikube?
04
How do I update my Spring Boot app on Minikube after code changes?
05
What's the difference between Minikube and kind for Spring Boot development?
N
Naren Founder & Principal Engineer

20+ years shipping production Java in banking & fintech. Everything here is grounded in real deployments.

Follow
Verified
production tested
July 15, 2026
last updated
2,398
articles · all by Naren
🔥

That's Spring Boot. Mark it forged?

5 min read · try the examples if you haven't

Previous
Disable Spring Data Auto-Configuration in Spring Boot
78 / 121 · Spring Boot
Next
Layered Docker Images with Spring Boot: Buildpacks and Efficient Dockerfiles