Ingress
An API object that manages external HTTP and HTTPS access to services inside a cluster. Ingress sits in front of multiple services and acts as a smart router — directing traffic based on hostnames, URL paths, or headers without exposing each service directly to the internet.
Ingress — The Cluster's Front Door
What Problem Does Ingress Solve?
Without Ingress, every service you want to expose externally needs its own LoadBalancer — and each LoadBalancer provisions a separate cloud IP. For Swiggy running 50+ microservices, that would mean 50 cloud IPs and 50 monthly billing line items.
Ingress fixes this with one entry point that routes smartly:
+------------------------------------------+| Internet |+------------------------------------------+ | v+------------------------------------------+| Ingress Controller (NGINX / Traefik) | <- One cloud LoadBalancer IP+------------------------------------------+ | | v v+-----------------+ +------------------+| /api/orders | | /api/payments || order-svc:80 | | payment-svc:80 |+-----------------+ +------------------+Core Components You Need to Know
Ingress Resource — The YAML configuration object where you define all routing rules.
Ingress Controller — The actual software process that reads those rules and enforces them. Common choices:
- NGINX Ingress Controller (most widely used, works on any cluster)
- Traefik (popular in self-managed clusters)
- AWS ALB Ingress Controller (native for EKS clusters on AWS)
RememberKubernetes does NOT ship with an Ingress Controller by default. Creating an Ingress resource without a controller does absolutely nothing. You must install a controller separately before any Ingress rules take effect.
A Real Ingress Resource — Explained Line by Line
apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: razorpay-routing namespace: production annotations: nginx.ingress.kubernetes.io/rewrite-target: / # Strips path prefix before forwarding to backendspec: ingressClassName: nginx # Which controller handles this Ingress tls: - hosts: - api.razorpay.in secretName: razorpay-tls-cert # TLS certificate stored as a Kubernetes Secret rules: - host: api.razorpay.in http: paths: - path: /payments pathType: Prefix # Matches /payments, /payments/upi, /payments/refund backend: service: name: payment-service port: number: 80 - path: /accounts pathType: Prefix backend: service: name: account-service port: number: 80Path Types — Common Confusion Point
| Type | Behavior | Example |
|---|---|---|
Prefix |
Matches any path starting with the value | /api matches /api/v1, /api/users |
Exact |
Must match the full path exactly | /api only matches /api — not /api/v1 |
ImplementationSpecific |
Controller decides the matching behavior | Avoid unless you know exactly what your controller does |
Common MistakeUsing
Exactwhen you meantPrefixcauses mysterious 404s on sub-paths. "Why doesn't/payments/upiwork?" — because your rule saysExact: /payments, which only matches the bare/paymentspath and nothing beneath it.
SSL Termination Flow
+------------------------------------------+| Browser sends HTTPS request | <- Encrypted TLS traffic+------------------------------------------+ | v+------------------------------------------+| Ingress Controller | <- Decrypts TLS using the Secret cert| (handles TLS handshake) |+------------------------------------------+ | v+------------------------------------------+| Backend Pod receives plain HTTP | <- No TLS complexity in app code+------------------------------------------+The Ingress Controller handles all TLS handshakes. Your backend pods only see plain HTTP traffic — which significantly simplifies your application code and certificate management.
Multi-Host Routing — One Ingress, Multiple Domains
A single Ingress resource can handle multiple hostnames, which is how PhonePe routes across its microservices:
rules: - host: api.phonepay.in http: paths: - path: / pathType: Prefix backend: service: name: api-gateway port: number: 80 - host: admin.phonepay.in http: paths: - path: / pathType: Prefix backend: service: name: admin-portal port: number: 80Quick Troubleshooting Commands
| Scenario | Command |
|---|---|
| List all Ingress rules across namespaces | kubectl get ingress -A |
| Inspect routing config in detail | kubectl describe ingress razorpay-routing -n production |
| Check Ingress Controller logs | kubectl logs -n ingress-nginx deploy/ingress-nginx-controller |
| Verify TLS certificate is attached | kubectl get secret razorpay-tls-cert -n production |
| Test path routing from inside cluster | kubectl run curl --image=curlimages/curl -it --rm -- curl http://payment-service/payments |
SecurityNever expose the Kubernetes API server, internal dashboards (Kubernetes Dashboard, Argo CD), or monitoring UIs (Grafana) via Ingress without authentication middleware. NGINX Ingress supports
nginx.ingress.kubernetes.io/auth-urlandauth-signinannotations for OAuth2 proxy integration — use them on every internal tool exposed externally.
TipAdd
nginx.ingress.kubernetes.io/ssl-redirect: "true"as an annotation to automatically redirect all HTTP traffic to HTTPS. Without this, both HTTP and HTTPS routes work — a common compliance issue on Razorpay-scale fintech platforms that require encrypted-only traffic.
Frequently Asked Questions
What problem does Ingress solve that a plain Service of type LoadBalancer doesn't?
Giving every Service a LoadBalancer means provisioning a separate cloud load balancer per service — expensive and unwieldy once you have more than a handful. Ingress lets one external load balancer (fronting an Ingress controller like nginx or ALB) route to many services based on hostname or URL path, so `api.example.com` and `app.example.com` can share a single entry point while still hitting different backend Services internally.
Why does adding an Ingress resource alone often do nothing?
Ingress is just an API spec — it requires an Ingress controller actually running in the cluster to watch Ingress objects and configure the underlying proxy (nginx, Traefik, AWS Load Balancer Controller, etc.). A common mistake is writing Ingress YAML in a fresh cluster and expecting traffic to route, when no controller was ever installed. Also worth knowing: different controllers support different annotations, so Ingress manifests aren't fully portable between controller implementations.