Skip to content

ingress-nginx

ingress-nginx is the Kubernetes Ingress controller. It routes external HTTP/S traffic to services inside the cluster based on hostname and path rules.

See ADR-005 for the full decision record.

How It Works

graph LR
    Browser -->|"http://grafana.local"| Tunnel["minikube tunnel\n127.0.0.1:80"]
    Tunnel --> nginx["ingress-nginx\ncontroller pod"]
    nginx -->|"matches Ingress rule\nhost: grafana.local"| Grafana["grafana Service\n(ClusterIP)"]

Minikube Setup: LoadBalancer + minikube tunnel

We use LoadBalancer type. On macOS with the Docker driver, the minikube IP (192.168.49.x) is inside Docker's bridge network and is not reachable from the host. minikube tunnel solves this by assigning 127.0.0.1 as the external IP.

Mode Access URL Requires
LoadBalancer (current) http://<hostname> (port 80) sudo minikube tunnel running
NodePort (old) http://<minikube-ip>:30080 Works on Linux; broken on macOS Docker driver

Start minikube tunnel

Run in a dedicated terminal and keep it open:

sudo minikube tunnel

Expected output:

✅  Tunnel successfully started
🔗  Starting tunnel for service ingress-nginx-controller.

Verify the external IP is assigned:

kubectl get svc ingress-nginx-controller -n ingress-nginx
# NAME                       TYPE           CLUSTER-IP    EXTERNAL-IP   PORT(S)
# ingress-nginx-controller   LoadBalancer   10.x.x.x      127.0.0.1     80:30080/TCP,443:30443/TCP

/etc/hosts

Point hostnames to 127.0.0.1 (not $(minikube ip)):

127.0.0.1  grafana.local keycloak.local

Add entries:

sudo sh -c 'echo "127.0.0.1  grafana.local keycloak.local" >> /etc/hosts'

Defining an Ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app
  annotations:
    cert-manager.io/cluster-issuer: "local-ca-issuer"  # optional TLS
spec:
  ingressClassName: nginx
  rules:
    - host: myapp.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-app
                port:
                  number: 8080
  tls:                          # optional
    - hosts:
        - myapp.local
      secretName: myapp-tls

Add the hostname to /etc/hosts:

echo "127.0.0.1  myapp.local" | sudo tee -a /etc/hosts

Then open http://myapp.local — no port suffix needed.

File Reference

infrastructure/controllers/ingress-nginx.yaml

Troubleshooting

Service unreachable / connection refused: - Is minikube tunnel running? Check with ps aux | grep tunnel - Is the hostname in /etc/hosts pointing to 127.0.0.1? - Is there an Ingress object for the hostname? kubectl get ingress -A

404 from nginx: - The tunnel is working but no Ingress matches the host — check the Ingress host: field - Test: curl -H "Host: myapp.local" http://127.0.0.1/ should return your app

minikube tunnel drops: - The tunnel process exits if the terminal is closed or the session times out - Restart with sudo minikube tunnel