Kubernetes NodePort Minikube: React Not Loading Fix
Troubleshoot Kubernetes NodePort not accessible on Minikube for React frontend served by Nginx. Fix blank pages, config substitution, networking issues with minikube service, port-forward, and proxy solutions.
Kubernetes NodePort Not Accessible on Minikube: React Frontend Not Loading
I’m deploying a React app frontend and backend on Minikube using Kubernetes. The frontend is served via Nginx in a Docker container, but nothing loads when accessing <minikube ip>:30100. How can I troubleshoot and fix NodePort access issues?
Frontend Dockerfile
# build
FROM node:18.19.1 AS build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# runtime
FROM nginx:alpine
# install envsubst
RUN apk add --no-cache gettext
COPY --from=build /app/build /usr/share/nginx/html
COPY public/config.js /usr/share/nginx/html/config.js
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
EXPOSE 80
CMD ["/entrypoint.sh"]
Entrypoint Script (entrypoint.sh)
#!/bin/sh
set -e
if [ -f /usr/share/nginx/html/config.js ]; then
envsubst '${BACKEND_URL}' < /usr/share/nginx/html/config.js > /tmp/config.js
mv /tmp/config.js /usr/share/nginx/html/config.js
fi
exec nginx -g 'daemon off;'
config.js in React App
window.RUNTIME_CONFIG = {
BACKEND_URL: "${BACKEND_URL}"
};
Usage in React App
const x = window.RUNTIME_CONFIG.BACKEND_URL;
Frontend Deployment Env Var
env:
- name: BACKEND_URL
value: http://server-service:8081
Frontend Service Ports
ports:
- protocol: TCP
port: 3000
targetPort: 80
nodePort: 30100
Backend CORS Allowed Origins (Go)
var allowedOrigins = map[string]bool{
"http://localhost:3000": true,
"http://localhost:30100": true,
os.Getenv("CLIENT_IP"): true,
"http://<nodeip>:30100": true,
"http://app-service:3000": true,
}
Issue: Page is blank or not loading at minikube ip:30100. Backend is at server-service:8081 internally. New to Kubernetes—need help debugging NodePort, service exposure, or config substitution.
Kubernetes NodePort Minikube: If your React frontend served by Nginx shows a blank page at
Contents
- Kubernetes NodePort and Minikube networking
- Nginx Kubernetes and runtime config substitution
- Step-by-step troubleshooting checklist
- Fixes: quick (dev) and robust (prod)
- Quick command reference
- Sources
- Conclusion
Kubernetes NodePort and Minikube networking
NodePort opens a port on each Kubernetes node and maps it to your service targetPort. In a real cluster that normally means you can hit
Common symptoms when NodePort isn’t reachable:
- curl
:30100 times out from your host - browser shows no network requests to static assets (or 504/timeout)
- the NodePort is present in the Service but Endpoints is empty (no pods matched)
What to check first
- Is the NodePort assigned and in the right range (30000–32767)? Run:
kubectl get svc frontend -o wide kubectl describe svc frontend
- Are there endpoints? If Endpoints is empty your Service selector doesn’t match the Deployment labels:
kubectl get endpoints frontend kubectl get pods --show-labels
- Is minikube driver blocking host access to the node IP? Try the minikube helper:
minikube ip minikube service frontend --url
The minikube service command gives a working URL (or starts a tunnel) appropriate for your environment; the docs explain nuances for different drivers: https://minikube.sigs.k8s.io/docs/handbook/accessing/.
If you get a connection from inside the minikube VM but not from your host, the problem is network reachability, not Kubernetes wiring.
Nginx Kubernetes and runtime config substitution
Two traps are common when serving a client bundle via Nginx inside Kubernetes.
- Wrong BACKEND_URL value for the browser
Your entrypoint does envsubst into /usr/share/nginx/html/config.js at container start. That yields a config file that the browser will fetch. In your Deployment you set:
env:
- name: BACKEND_URL
value: http://server-service:8081
But note: server-service is an internal DNS name that resolves only inside the cluster (pods). The React app runs in the user’s browser outside the cluster, so requests to http://server-service:8081 will fail. That commonly produces a blank page when the app hits unhandled runtime errors or when API calls error out and UI never initializes.
Check the actual value served to the browser:
- Open in your browser: http://
:30100/config.js - Or exec into the frontend pod and inspect:
kubectl exec -it <frontend-pod> -- cat /usr/share/nginx/html/config.js
If the file contains “http://server-service:8081”, that’s the root problem for browser-to-API connectivity.
- Nginx not serving files / permission or entrypoint issues
Look at the frontend pod logs. If entrypoint fails, nginx won’t serve content:
kubectl logs <frontend-pod>
Confirm Nginx serves index.html from the expected path:
kubectl exec -it <frontend-pod> -- curl -sS -I http://localhost/
Quick diagnosis question: does config.js load in the browser? Check Network tab in DevTools for /config.js and for requests to your BACKEND_URL. Browser console will show CORS or network errors.
Step-by-step troubleshooting checklist
- Verify pods and service exist
kubectl get pods -o wide kubectl get svc frontend -o wide
- Check the Service wiring
kubectl describe svc frontend kubectl get endpoints frontend
- If Endpoints is empty -> Service selector mismatch. Compare Deployment labels to the Service selector.
- Check that Nginx built and the entrypoint ran
kubectl logs <frontend-pod>
kubectl exec -it <frontend-pod> -- ls -la /usr/share/nginx/html
kubectl exec -it <frontend-pod> -- cat /usr/share/nginx/html/config.js
- Test connectivity from inside the cluster (pod -> backend)
kubectl exec -it <frontend-pod> -- curl -sS http://server-service:8081/health || true
If this works, the cluster DNS and service are fine.
- Test NodePort from the minikube node
minikube ip # get node IP
curl -v http://$(minikube ip):30100 || true
# or, from inside minikube VM:
minikube ssh -- curl -v http://localhost:30100 || true
If curl inside minikube returns content but host curl times out, it’s a host→VM networking issue.
- Check browser DevTools
- Look for 404/500 for static files, missing /config.js, or CORS errors when calling the API.
- Inspect backend CORS settings
Your Go allowedOrigins map must contain the exact origin the browser uses, e.g. http://:30100. For local dev you can temporarily relax CORS to simplify debugging.
Fixes: quick (dev) and robust (prod)
Option A — Fast dev workaround (no config changes)
- Use minikube’s helpers:
minikube service frontend --url
# or port-forward:
kubectl port-forward svc/frontend 30100:80
# then open http://localhost:30100
These make the frontend reachable without changing app config. For backend testing:
kubectl port-forward svc/server-service 8081:8081
# point the browser or config.js to http://localhost:8081
Reference: https://minikube.sigs.k8s.io/docs/handbook/accessing/
Option B — Make the frontend’s API URL resolvable by the browser
Update the front-end config at runtime to an address the browser can reach:
kubectl set env deployment/frontend BACKEND_URL="http://$(minikube ip):<backend-nodePort>"
# This triggers a rolling restart so envsubst runs with the new value.
Caveat: this is fine for local testing, not ideal for production.
Option C — Proxy API through Nginx (recommended for single-origin behavior)
Modify your Nginx to proxy /api to the internal service. Then set BACKEND_URL to a relative path (e.g. “/api”). The browser remains same-origin and CORS is avoided.
Example nginx snippet to include in the image:
server {
listen 80;
location / {
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://server-service:8081/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Then set config.js to:
window.RUNTIME_CONFIG = { BACKEND_URL: "/api" };
This is robust: client calls /api, Nginx forwards to the internal service name that only needs to resolve inside the cluster.
Option D — Use Ingress or LoadBalancer
- Enable ingress in minikube and create an Ingress resource to route a host to services. Ingress avoids NodePort pain and lets you use hostnames:
minikube addons enable ingress
# apply an Ingress manifest mapping host/myapp.local -> frontend service and /api -> backend
Alternatively use minikube tunnel with a LoadBalancer service. See minikube issues and examples where NodePort is not accessible under some drivers: https://github.com/kubernetes/minikube/issues/9499 and the Stack Overflow threads on expose/NodePort behavior.
Quick command reference
- Show services, ports, NodePort:
kubectl get svc kubectl describe svc frontend
- Show endpoints (should not be empty):
kubectl get endpoints frontend
- View runtime config.js in the pod:
kubectl exec -it $(kubectl get pods -l app=frontend -o jsonpath='{.items[0].metadata.name}') -- cat /usr/share/nginx/html/config.js
- Test cluster-internal connectivity:
kubectl exec -it <frontend-pod> -- curl -sS http://server-service:8081/health
- Make frontend reachable on localhost (dev):
kubectl port-forward svc/frontend 30100:80
# or
minikube service frontend --url
- Set BACKEND_URL to minikube IP (dev):
kubectl set env deployment/frontend BACKEND_URL="http://$(minikube ip):<backend-nodePort>"
- Enable ingress on minikube:
minikube addons enable ingress
Sources
- Kubernetes NodePort on Minikube not accessible from outside - Stack Overflow
- Pod not accessible on NodePort via the Windows 10 host machine · Issue #9499 · kubernetes/minikube · GitHub
- Expose port in minikube - Stack Overflow
- Accessing apps | minikube
- Connecting NodePort in Minikube - Server Fault
Conclusion
Kubernetes NodePort Minikube problems usually boil down to networking reachability or incorrect runtime configuration for browser clients—especially when the frontend’s BACKEND_URL points at an internal service name. Inspect endpoints, pod logs, and the substituted /config.js; for local workarounds use minikube service or kubectl port-forward, and for a robust solution proxy API calls through Nginx or use Ingress so the browser talks to an address it can actually resolve.