Kubernetes Ingress: NGINX, Path Routing and TLS Configuration (2026)

Kubernetes Ingress is an API object that manages external HTTP/HTTPS access to services inside the cluster. Unlike a LoadBalancer Service (which provisions one cloud load balancer per service), a single Ingress controller handles routing for all services — dramatically reducing cost and complexity. This guide covers the NGINX Ingress Controller, path and host-based routing, TLS with cert-manager, and production annotations.

1. Ingress vs LoadBalancer Service

FeatureLoadBalancer ServiceIngress
Cloud LB per service1 per service ($$$)1 shared controller
HTTP routingNo (Layer 4 only)Yes (Layer 7)
TLS terminationManualAutomatic with cert-manager
Path routingNoYes
Host routingNoYes
Pro Tip: In production, use one NGINX Ingress Controller with Ingress objects for all HTTP/HTTPS services. Reserve LoadBalancer Services only for non-HTTP traffic (TCP/UDP) like databases or gRPC.

2. Installing NGINX Ingress Controller

# Add the ingress-nginx Helm repo
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

# Install in its own namespace
helm install ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx \
  --create-namespace \
  --set controller.replicaCount=2 \
  --set controller.nodeSelector."kubernetes\.io/os"=linux \
  --set controller.service.externalTrafficPolicy=Local

# Verify — get the external IP
kubectl get svc -n ingress-nginx ingress-nginx-controller

3. Path-Based Routing

Route /api/* to the API service and / to the frontend:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp-ingress
  namespace: production
  annotations:
    nginx.ingress.kubernetes.io/use-regex: "true"
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.example.com
    http:
      paths:
      - path: /api(/|$)(.*)
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 8080
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend-service
            port:
              number: 3000

4. Host-Based Routing

Route different domains to different services — e.g., api.example.com and app.example.com:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: multi-host-ingress
  namespace: production
spec:
  ingressClassName: nginx
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 8080
  - host: app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend-service
            port:
              number: 3000

5. TLS with cert-manager

cert-manager automates TLS certificate provisioning from Let's Encrypt (free) or any ACME CA:

# Install cert-manager
helm repo add jetstack https://charts.jetstack.io
helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager --create-namespace \
  --set installCRDs=true
# Create a ClusterIssuer for Let's Encrypt
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: admin@example.com
    privateKeySecretRef:
      name: letsencrypt-prod-key
    solvers:
    - http01:
        ingress:
          ingressClassName: nginx
---
# Ingress with automatic TLS
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: secure-ingress
  namespace: production
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - myapp.example.com
    secretName: myapp-tls-cert
  rules:
  - host: myapp.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend-service
            port:
              number: 3000
Note: cert-manager will automatically create the TLS secret myapp-tls-cert and renew the certificate before it expires. Check certificate status with: kubectl get certificate -n production

6. Useful Annotations

metadata:
  annotations:
    # Rate limiting — 100 requests per second per IP
    nginx.ingress.kubernetes.io/limit-rps: "100"

    # Increase timeouts for long-running requests
    nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "300"

    # Enable CORS
    nginx.ingress.kubernetes.io/enable-cors: "true"
    nginx.ingress.kubernetes.io/cors-allow-origin: "https://myapp.example.com"

    # Sticky sessions (cookie-based)
    nginx.ingress.kubernetes.io/affinity: "cookie"
    nginx.ingress.kubernetes.io/session-cookie-name: "route"
    nginx.ingress.kubernetes.io/session-cookie-expires: "172800"

    # Custom max body size (default 1m)
    nginx.ingress.kubernetes.io/proxy-body-size: "50m"

    # Force HTTPS redirect
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"

7. IngressClass

IngressClass allows multiple Ingress controllers in one cluster — e.g., NGINX for general traffic and AWS Load Balancer Controller for internal services:

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: nginx
  annotations:
    ingressclass.kubernetes.io/is-default-class: "true"
spec:
  controller: k8s.io/ingress-nginx

Set is-default-class: "true" to make it the default so Ingress objects without an explicit ingressClassName use it automatically.

Frequently Asked Questions

How do I debug an Ingress that isn't routing traffic?

Check in order: 1) kubectl get ingress -n namespace — does it show an ADDRESS? 2) kubectl describe ingress name — are backends resolved? 3) kubectl logs -n ingress-nginx deploy/ingress-nginx-controller — check for errors. 4) Verify the backend Service exists and has healthy Endpoints.

What is the difference between pathType Prefix and Exact?

Exact matches only that exact path. Prefix matches the path and all paths beginning with it. ImplementationSpecific lets the controller decide — avoid this for portability.

Can I use Ingress with gRPC?

Yes — add annotation nginx.ingress.kubernetes.io/backend-protocol: "GRPC" and use TLS (gRPC requires HTTP/2, which NGINX only enables on TLS connections).

How do I handle WebSocket connections?

NGINX Ingress handles WebSocket automatically for most cases. Set nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" and proxy-send-timeout: "3600" to prevent idle WebSocket connections from being dropped.