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.
Table of Contents
1. Ingress vs LoadBalancer Service
| Feature | LoadBalancer Service | Ingress |
|---|---|---|
| Cloud LB per service | 1 per service ($$$) | 1 shared controller |
| HTTP routing | No (Layer 4 only) | Yes (Layer 7) |
| TLS termination | Manual | Automatic with cert-manager |
| Path routing | No | Yes |
| Host routing | No | Yes |
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
myapp-tls-cert and renew the certificate before it expires. Check certificate status with: kubectl get certificate -n production6. 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.