Kubernetes Ingress Controllers: Routing External Traffic the Right Way
Compare Kubernetes Ingress controllers -- nginx-ingress, Traefik, HAProxy, Contour. Learn path and host routing, automatic TLS with cert-manager, and the Gateway API.
Infrastructure engineer with 10+ years building production systems on AWS, GCP,…

Getting Traffic Into Your Cluster
A Kubernetes Ingress controller is the component that actually routes external HTTP/HTTPS traffic to your Services. Kubernetes defines the Ingress resource as a declarative routing spec, but it does absolutely nothing on its own -- you need a controller running in the cluster to read those specs and configure a reverse proxy accordingly.
This distinction trips up nearly every newcomer. You create an Ingress manifest, apply it, and nothing happens. No error, no traffic, no feedback. That's because without a controller, an Ingress resource is just data sitting in etcd.
What Is a Kubernetes Ingress?
Definition: A Kubernetes Ingress is an API resource that defines rules for routing external HTTP(S) traffic to internal Services based on hostnames and URL paths. It requires an Ingress controller -- a reverse proxy running inside the cluster -- to implement these rules and handle the actual traffic routing, TLS termination, and load balancing.
Why Not Just Use a LoadBalancer Service?
You can expose each Service with type: LoadBalancer, and it works. But each one provisions a separate cloud load balancer, each with its own external IP and monthly cost. For 10 services, that's 10 load balancers at $15-20/month each on most clouds. An Ingress controller gives you one load balancer that routes to all your services based on rules.
| Approach | External IPs | TLS Management | Cost (10 services, AWS) |
|---|---|---|---|
| LoadBalancer per Service | 10 | Per-service | ~$180/month |
| Single Ingress Controller | 1 | Centralized | ~$18/month |
Popular Ingress Controllers Compared
| Controller | Proxy Engine | Protocol Support | Config Style | Best For |
|---|---|---|---|---|
| ingress-nginx | NGINX | HTTP, HTTPS, gRPC, WebSocket | Annotations | General purpose, most popular |
| Traefik | Traefik | HTTP, HTTPS, gRPC, TCP, UDP | CRDs + annotations | Auto-discovery, middleware chains |
| HAProxy Ingress | HAProxy | HTTP, HTTPS, TCP | Annotations + ConfigMap | High-performance TCP workloads |
| Contour | Envoy | HTTP, HTTPS, gRPC | HTTPProxy CRD | Multi-team clusters, delegation |
| Kong Ingress | Kong (OpenResty) | HTTP, HTTPS, gRPC, TCP | CRDs + plugins | API gateway features built in |
Pro tip: Start with ingress-nginx unless you have a specific reason not to. It's the most widely deployed, has the most community support, and handles 90% of use cases. Switch to Traefik if you need automatic Let's Encrypt or TCP/UDP routing without extra configuration.
Setting Up ingress-nginx: Step by Step
- Install the controller using Helm or the official manifests
- Verify the controller Pod is running and a LoadBalancer Service is created
- Create an Ingress resource with your routing rules
- Point your DNS to the controller's external IP
- Add TLS with cert-manager for automatic certificate management
# Install with Helm
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx --create-namespace
# Verify the controller is running
kubectl get pods -n ingress-nginx
kubectl get svc -n ingress-nginx
Path-Based Routing
Route different URL paths to different backend Services. This is the most common Ingress pattern:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
ingressClassName: nginx
rules:
- host: myapp.example.com
http:
paths:
- path: /api(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: api-service
port:
number: 8080
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 3000
Host-Based Routing
Route different hostnames to different Services. Useful for microservices or multi-tenant architectures:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: multi-host-ingress
spec:
ingressClassName: nginx
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
- host: dashboard.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: dashboard-service
port:
number: 3000
TLS with cert-manager
cert-manager automates certificate issuance and renewal from Let's Encrypt (or other ACME providers). It watches Ingress resources for TLS configuration and automatically provisions certificates.
# Install cert-manager
helm repo add jetstack https://charts.jetstack.io
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager --create-namespace \
--set crds.enabled=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
solvers:
- http01:
ingress:
class: nginx
# Ingress with automatic TLS
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: secure-ingress
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- hosts:
- myapp.example.com
secretName: myapp-tls
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 3000
Watch out: Always test with Let's Encrypt's staging server first (
https://acme-staging-v02.api.letsencrypt.org/directory). The production server has strict rate limits -- 50 certificates per registered domain per week. Hit that limit and you're locked out for 7 days.
Common Annotations for ingress-nginx
| Annotation | Purpose | Example Value |
|---|---|---|
nginx.ingress.kubernetes.io/rewrite-target | Rewrite URL path | /$2 |
nginx.ingress.kubernetes.io/ssl-redirect | Force HTTPS | "true" |
nginx.ingress.kubernetes.io/proxy-body-size | Max request body | "50m" |
nginx.ingress.kubernetes.io/proxy-read-timeout | Backend timeout | "120" |
nginx.ingress.kubernetes.io/cors-allow-origin | CORS origin header | "https://mysite.com" |
nginx.ingress.kubernetes.io/rate-limit-rps | Requests per second | "10" |
Gateway API: The Future of Ingress
The Gateway API is an official Kubernetes project that's gradually replacing Ingress. It addresses several Ingress limitations: no standard for TCP/UDP routing, vendor-specific annotations, and no role-based resource model.
Key Differences from Ingress
- Typed routes -- HTTPRoute, TCPRoute, GRPCRoute instead of one generic Ingress resource
- Role-oriented -- GatewayClass (infra provider), Gateway (cluster operator), Routes (app developer)
- Portable -- behavior defined in the spec, not in vendor annotations
- Extensible -- policy attachment for things like rate limiting, authentication, and circuit breaking
# Gateway API example
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: app-route
spec:
parentRefs:
- name: my-gateway
hostnames:
- "myapp.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: api-service
port: 8080
Pricing and Cost Comparison
| Solution | Cost | Notes |
|---|---|---|
| ingress-nginx (OSS) | Free + 1 cloud LB (~$18/mo) | Most popular, community maintained |
| Traefik (OSS) | Free + 1 cloud LB | Built-in Let's Encrypt, dashboard |
| Traefik Enterprise | Custom pricing | HA, distributed rate limiting, OIDC |
| Kong Gateway (OSS) | Free + 1 cloud LB | Plugin ecosystem for API management |
| Kong Enterprise | From $15K/year | Developer portal, advanced analytics |
| AWS ALB Ingress Controller | ALB pricing (~$22/mo + LCU) | Native AWS integration, no extra pods |
Frequently Asked Questions
What happens if I create an Ingress without an Ingress controller?
Nothing. The Ingress resource is stored in etcd, but no component reads or acts on it. There's no error message or warning -- Kubernetes accepts the resource. You need a controller running in the cluster to watch for Ingress resources and configure the actual reverse proxy.
Can I run multiple Ingress controllers in one cluster?
Yes. Use the ingressClassName field in your Ingress resource to specify which controller should handle it. This is common in large clusters where different teams or environments need different controller configurations. Each controller watches only for Ingress resources with its class name.
Is the Gateway API ready for production?
The core HTTPRoute and Gateway resources reached GA (v1) status in Kubernetes 1.28. Most major controllers (ingress-nginx, Traefik, Cilium, Contour) support Gateway API. It's production-ready for HTTP routing. TCP/UDP/gRPC routes are still evolving. New projects should consider starting with Gateway API directly.
How do I handle WebSocket connections through an Ingress?
ingress-nginx supports WebSockets out of the box -- no special configuration needed. For long-lived connections, increase the proxy timeouts: set nginx.ingress.kubernetes.io/proxy-read-timeout and proxy-send-timeout to higher values (e.g., "3600" for one hour). Traefik also handles WebSockets natively.
Should I use annotations or CRDs for Ingress configuration?
Annotations work for simple cases but become unmanageable with complex routing. If you need middleware chains, traffic splitting, or cross-namespace routing, use a controller with CRD support (Traefik IngressRoute, Contour HTTPProxy, or Gateway API HTTPRoute). CRDs are typed, validated, and version-controlled.
How do I migrate from Ingress to Gateway API?
Run both side by side. Install a Gateway API implementation, create equivalent HTTPRoute resources, and test them with a separate Gateway. Once verified, update your DNS to point to the new Gateway's external IP. Remove the old Ingress resources after traffic has fully migrated. Most controllers support both APIs simultaneously.
Conclusion
Install ingress-nginx, add cert-manager for automatic TLS, and use path-based or host-based routing. That covers 90% of real-world traffic routing needs. Keep your annotations minimal and well-documented. If you're starting a new cluster in 2024+, evaluate Gateway API -- it's the direction Kubernetes networking is heading, and the HTTP routing parts are production-ready today.
Written by
Abhishek Patel
Infrastructure engineer with 10+ years building production systems on AWS, GCP, and bare metal. Writes practical guides on cloud architecture, containers, networking, and Linux for developers who want to understand how things actually work under the hood.
Related Articles
Certificate Management at Scale: Let's Encrypt, ACME, and cert-manager
Automate TLS certificates with Let's Encrypt, ACME protocol, and cert-manager in Kubernetes. Covers HTTP-01, DNS-01, wildcards, private CAs, and expiry monitoring.
9 min read
SecuritySecret Management: HashiCorp Vault vs AWS Secrets Manager vs Kubernetes Secrets
Compare Vault, AWS Secrets Manager, and Kubernetes Secrets. Learn about dynamic secrets, rotation, injection patterns, and when to use each tool.
9 min read
NetworkingDNS Explained: From Domain Name to IP Address, Step by Step
DNS translates domain names into IP addresses in milliseconds. Trace the full resolution chain step by step, learn every record type, and debug common failures like NXDOMAIN and SERVFAIL.
12 min read
Enjoyed this article?
Get more like this in your inbox. No spam, unsubscribe anytime.