Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Kubernetes Ingress Controllers Explained

By Kokil Thapa | Last reviewed: September 2026

Kubernetes Ingress Controllers Explained starts with a gap most teams hit after their first deploy: Pods are reachable inside the cluster, but nobody on the internet can hit your app by hostname. A Service of type LoadBalancer gives you one IP per app. That gets expensive fast. An Ingress sits on top of your cluster networking layer and maps hostnames and URL paths to backend Services. The controller is the process that watches those rules and programs a real reverse proxy. This guide walks through how that works, which controller to pick, and what breaks in production.

What is a Kubernetes ingress controller and how does it work?

An Ingress resource in Kubernetes is declarative routing configuration. It does nothing by itself. The ingress controller is a Pod (often a Deployment) that watches Ingress objects across the cluster. When rules change, the controller updates its proxy config and reloads.

Think of it as two layers. The API layer stores intent. The controller layer enforces it on the data plane.

Ingress Controller Data PlaneClientHTTPS requestIngress ControllerNGINX / Traefik / HAProxyServiceClusterIPPod APod BIngress Resourcehost + path rules
Kubernetes Ingress Controllers Explained: the controller reads Ingress rules and forwards traffic to Services and Pods.

The control loop in plain terms

Every ingress controller runs the same basic loop:

  1. Watch Ingress, Service, EndpointSlice, and sometimes Secret objects via the Kubernetes API.
  2. Build an internal model of which hostname and path map to which backend port.
  3. Render that model into proxy configuration (NGINX conf, Envoy xDS, Traefik dynamic config).
  4. Apply the config and signal a graceful reload so existing connections stay alive.

If the controller Pod dies, another replica takes over. Existing connections may drop briefly unless you run multiple replicas behind an external load balancer. That HA pattern matters for production clusters serving real traffic.

The official Kubernetes documentation describes Ingress as an API specification, not a built-in implementation. You must install a controller separately. Managed clusters often ship one (AWS Load Balancer Controller on EKS, GCE Ingress on GKE). Self-managed clusters on Ubuntu with Linux system administration duties fall on your team to pick and maintain one.

How does an ingress controller differ from a Kubernetes Service?

Services provide stable cluster-internal networking. An Ingress adds L7 routing on top. They solve different problems and are usually used together.

FeatureService (ClusterIP / NodePort / LB)Ingress + Controller
OSI layerL4 (TCP/UDP ports)L7 (HTTP host, path, headers)
RoutingOne Service = one port mappingMany hostnames/paths → many Services
TLS terminationNot built in (passthrough only on some LBs)Native HTTPS with cert references
Cost on cloudLoadBalancer Service = one cloud LB eachOne LB fronting one controller for many apps
Typical useInternal microservice mesh, DB proxiesPublic web apps, APIs, admin panels

A common pattern on a production Laravel API I have worked on: three Deployments, three ClusterIP Services, one Ingress with three path rules. One external IP handles api.example.com/v1, api.example.com/admin, and docs.example.com. Without Ingress you would pay for three cloud load balancers at roughly Rs 3,000–5,000/month each (~USD 22–37).

NodePort exposes a port on every worker. That works for lab setups with Minikube or kind. It is a poor public entry point. You still need something to distribute traffic and terminate TLS.

Which Kubernetes ingress controller should you choose in 2026?

No single controller wins every scenario. Pick based on cloud provider, team skills, and whether you need advanced traffic management.

Ingress Controller ChoiceWhere does the cluster run?Managed cloudUse vendor controllerBare metal / VPSNGINX + MetalLBEdge / smallTraefik on k3sNeed mTLS / WAF?Consider Istio gatewaySimple HTTP apps?ingress-nginx is fine
Choosing an ingress controller: cloud vendor, bare-metal constraints, and L7 feature needs drive the decision.
ControllerBest fitTrade-offs
ingress-nginxGeneral-purpose HTTP, largest community, works everywhereConfig via annotations can sprawl; reload-based, not hot xDS
Traefikk3s default, auto Let's Encrypt, good for small teamsComplex routing rules harder to debug at scale
HAProxy IngressHigh throughput, low latency, familiar HAProxy opsSmaller ecosystem than NGINX for K8s-specific docs
AWS LB ControllerEKS with ALB/NLB integration, target-type IPAWS-only; IngressClass semantics differ from NGINX
Cilium IngressClusters already on Cilium eBPF networkingYounger feature set vs mature NGINX annotations
Gateway APINew projects wanting typed, extensible L4/L7 routesMigration path from classic Ingress still evolving

Gateway API is the long-term direction Kubernetes SIG Network is pushing. It splits concerns into GatewayClass, Gateway, and HTTPRoute resources. Many teams still run classic Ingress in 2026 because every controller supports it and the YAML is well understood.

For a booking platform like Adventure Third Pole Trek, I would default to ingress-nginx on a VPS cluster with MetalLB unless the client is already on EKS. It is boring, documented, and easy to hand off.

How do you install and configure an NGINX Ingress Controller?

The community-maintained ingress-nginx project is the most copied install path. Below is a minimal production-oriented setup on a generic cluster.

Step 1: Install the controller

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.12.0/deploy/static/provider/cloud/deploy.yaml

Verify the controller Pod is running and the Service has an external address:

kubectl get pods -n ingress-nginx
kubectl get svc -n ingress-nginx ingress-nginx-controller

On bare metal without a cloud LB, pair this with MetalLB in L2 mode so the controller Service gets a routable IP on your LAN or public subnet.

Step 2: Define an IngressClass

IngressClass tells the cluster which controller should reconcile a given Ingress. Modern manifests include one by default named nginx.

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: nginx
spec:
  controller: k8s.io/ingress-nginx

Step 3: Create a sample Ingress

This routes shop.example.com to a Service named web on port 80:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: shop-ingress
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: "20m"
spec:
  ingressClassName: nginx
  rules:
    - host: shop.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 80

Validate YAML structure with a JSON or YAML formatter before applying. A single indentation error sends you hunting through controller logs.

Step 4: Add TLS with cert-manager

HTTPS belongs at the Ingress layer for most web apps. Install cert-manager, create a ClusterIssuer for Let's Encrypt, then reference a TLS Secret in the Ingress. Full walkthrough is in the companion post on Kubernetes Ingress and TLS with cert-manager.

spec:
  tls:
    - hosts:
        - shop.example.com
      secretName: shop-example-com-tls
  rules:
    - host: shop.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 80

Point your DNS A record at the controller's external IP. Wait for the ACME HTTP-01 or DNS-01 challenge to complete. Test with curl -I https://shop.example.com before announcing go-live.

TLS via cert-managerIngresscert-managerLet's EncryptTLS Secret1. Certificate requested2. ACME challenge3. Cert issued4. Ingress controller mounts Secret and serves HTTPSnginx reads tls.crt + tls.key on reload
cert-manager automates TLS certificate lifecycle for Ingress resources in Kubernetes.

Annotations that matter in production

  • nginx.ingress.kubernetes.io/proxy-body-size — raise for file uploads on legal-tech portals with document uploads.
  • nginx.ingress.kubernetes.io/rate-limit — basic abuse protection on public forms.
  • nginx.ingress.kubernetes.io/ssl-redirect — force HTTPS when TLS is configured.
  • nginx.ingress.kubernetes.io/whitelist-source-range — lock admin paths to office IPs.

Store sensitive values in Kubernetes Secrets done right, not in annotation plaintext. For a Laravel app on Kubernetes, set APP_URL to the public HTTPS hostname the Ingress exposes.

What are common mistakes when running ingress controllers in production?

Most ingress outages I have seen are configuration or capacity issues, not Kubernetes bugs. The controller is a single choke point. Treat it accordingly.

Production PitfallsBefore (broken)• No IngressClass set• Controller has no CPU limits• 502 from wrong Service portAfter (stable)• ingressClassName: nginx• requests/limits + 2 replicas• targetPort matches containerPortOps checklistHPA on controller DeploymentNetworkPolicy for ingress namespaceVelero backup of Ingress YAMLMonitor 5xx rate at proxy layer
Kubernetes Ingress Controllers Explained: fix IngressClass, resource limits, and port alignment before scaling traffic.

Wrong or missing IngressClass

If no controller claims your Ingress, it sits in limbo. No address, no routing. Always set spec.ingressClassName explicitly. Multiple controllers in one cluster require distinct IngressClass names.

Service port mismatches

The Ingress backend port is the Service port number, not the container port. A Service exposing port 80 that targets containerPort 8080 is valid. Pointing the Ingress at 8080 when the Service only exposes 80 yields 502 Bad Gateway. Check with kubectl describe ingress and kubectl get endpoints.

Undersized controller pods

TLS termination and gzip eat CPU. Set resource requests and limits on the controller Deployment. Run at least two replicas. Use horizontal pod autoscaling if traffic spikes during campaigns.

Ignoring network policy

Lock down the ingress namespace so only the controller can reach app Pods on their Service ports. Broader policies are covered in Kubernetes network policies explained.

Skipping backup of routing config

Ingress objects live in etcd. A bad kubectl delete --all ingress hurts. GitOps with Argo CD or plain Git-backed manifests plus Velero backup gives you a restore path.

For teams building REST APIs in Nepal or globally, the ingress layer is also where you attach CORS headers, request size limits, and optional WAF rules before traffic hits PHP-FPM or Node workers.

Key Takeaways

  • An Ingress is a routing rule; the ingress controller is the daemon that enforces it on a reverse proxy.
  • Use ClusterIP Services for backends and a single Ingress controller to multiplex many hostnames through one load balancer.
  • ingress-nginx remains the default choice for self-managed clusters; use vendor controllers on EKS, GKE, and AKS.
  • Always set ingressClassName, match Service ports correctly, and run at least two controller replicas with resource limits.
  • Automate TLS with cert-manager and store certificates in Secrets referenced by the Ingress spec.
  • Monitor 5xx rates at the proxy, back up Ingress manifests, and restrict traffic with NetworkPolicy.

People Also Ask

Do I need an ingress controller if I use a cloud LoadBalancer Service?

Not strictly, but you will spend more and lose L7 features. A LoadBalancer per Service works for one or two apps. Beyond that, an ingress controller saves cost and centralises hostname routing, TLS, and redirects.

What is the difference between Ingress and Gateway API?

Ingress is a single resource with annotations for extensions. Gateway API splits routing into GatewayClass, Gateway, and route types with clearer role separation. Both need a controller. Ingress is still the practical default in 2026; Gateway API is the forward path.

Can one ingress controller handle gRPC and WebSockets?

Yes. NGINX and Traefik support WebSocket upgrade headers and gRPC over HTTP/2. You may need specific annotations for long-lived connections and timeout values higher than the defaults.

How do I debug a 502 Bad Gateway from my ingress controller?

Check Endpoints first: empty endpoints mean no healthy Pods. Then verify Service port vs containerPort. Finally read controller logs with kubectl logs -n ingress-nginx deploy/ingress-nginx-controller. Most 502s are backend connectivity, not Ingress syntax.

Put ingress routing on solid ground

Kubernetes Ingress Controllers Explained boils down to one idea: separate routing intent from the proxy that executes it. Pick a controller that matches your infrastructure, front it with automated TLS, and treat the controller Deployment as production-critical infrastructure. If you are moving a Laravel or eCommerce workload onto Kubernetes and want the ingress, TLS, and ongoing maintenance handled end to end, get in touch via the contact page. For related reading, see debugging CrashLoopBackOff, enterprise application development, and Quick And Easy Nepalese Grocery for a Laravel eCommerce reference deployment.

Frequently Asked Questions

An Ingress is declarative routing configuration stored in the Kubernetes API. It does nothing alone. The ingress controller is a Pod, usually a Deployment, that watches Ingress objects and programs a reverse proxy or load balancer to forward HTTP and HTTPS traffic to the right Services and Pods.

Not strictly. One LoadBalancer per Service works for one or two apps. Beyond that you pay for each cloud load balancer separately and lose centralised L7 routing, TLS termination, and redirects. An ingress controller multiplexes many hostnames through one external entry point.

Services provide stable cluster-internal networking at L4, mapping ports to Pods. An Ingress adds L7 routing on top, mapping hostnames and URL paths to multiple backend Services. They solve different problems and are typically used together: ClusterIP Services for backends, one Ingress controller as the public HTTP entry point.

No single controller wins every scenario. Pick based on cloud provider, team skills, and L7 feature needs. ingress-nginx suits general-purpose HTTP on any cluster. Traefik fits small teams wanting auto Let's Encrypt. AWS Load Balancer Controller is the EKS-native choice. Gateway API is the long-term direction, but classic Ingress remains the practical default because every controller supports it and the YAML is well understood.

Apply the community ingress-nginx manifest with kubectl apply -f against the controller-v1.12.0 cloud deploy YAML. Verify the controller Pod is running in the ingress-nginx namespace and that the controller Service has an external address. On bare metal without a cloud load balancer, pair it with MetalLB in L2 mode so the controller Service gets a routable IP on your LAN or public subnet.

IngressClass tells the cluster which controller should reconcile a given Ingress resource. Modern ingress-nginx manifests include one named nginx with controller k8s.io/ingress-nginx. Always set spec.ingressClassName explicitly on your Ingress. If no controller claims your Ingress, it sits in limbo with no address and no routing. Multiple controllers in one cluster require distinct IngressClass names.

Install cert-manager, create a ClusterIssuer for Let's Encrypt, then reference a TLS Secret in the Ingress spec under tls with hosts and secretName. Point your DNS A record at the controller external IP, wait for the ACME HTTP-01 or DNS-01 challenge to complete, and test with curl -I https://your-hostname before go-live. cert-manager automates certificate lifecycle for Ingress resources.

Each cloud LoadBalancer Service costs roughly Rs 3,000 to 5,000 per month, about USD 22 to 37. Without Ingress, three public apps mean three load balancers. One ingress controller fronting ClusterIP Services lets one external IP handle api.example.com, api.example.com/admin, and docs.example.com, cutting recurring cloud networking cost significantly.

Wrong or missing IngressClass leaves Ingress objects unreconciled. Service port mismatches cause 502 Bad Gateway when the Ingress backend port does not match the Service port number. Undersized controller Pods struggle under TLS termination and gzip load. Skipping NetworkPolicy leaves app Pods broadly reachable. Deleting all Ingress objects without GitOps backup removes routing config stored in etcd with no quick restore path.

Ingress is a single resource extended via annotations. Gateway API splits routing into GatewayClass, Gateway, and HTTPRoute resources with clearer role separation. Both require a controller to enforce rules. Ingress remains the practical default in 2026 because support is universal and YAML is well understood. Gateway API is the forward path Kubernetes SIG Network is pushing.

Yes. NGINX and Traefik support WebSocket upgrade headers and gRPC over HTTP/2. You may need specific annotations for long-lived connections and timeout values higher than defaults. This matters for real-time features and RPC-style APIs routed through the same controller that serves ordinary HTTP traffic.

Check Endpoints first because empty endpoints mean no healthy Pods behind the Service. Then verify the Ingress backend port matches the Service port number, not the container port. A Service exposing port 80 that targets containerPort 8080 is valid, but pointing the Ingress at 8080 when the Service only exposes 80 yields 502. Read controller logs with kubectl logs against the ingress-nginx-controller Deployment.

NodePort exposes a port on every worker node. That works for lab setups with Minikube or kind, but you still need something to distribute traffic and terminate TLS for production. Ingress centralises hostname and path routing plus HTTPS at a single controlled entry point instead of scattering ports across the cluster.

TLS termination and gzip consume CPU, so set resource requests and limits on the controller Deployment. Run at least two replicas. Use horizontal pod autoscaling if traffic spikes during campaigns. If a controller Pod dies, another replica takes over, though existing connections may drop briefly unless multiple replicas sit behind an external load balancer. Treat the controller as production-critical infrastructure.

nginx.ingress.kubernetes.io/proxy-body-size raises upload limits for apps handling document uploads. nginx.ingress.kubernetes.io/rate-limit adds basic abuse protection on public forms. nginx.ingress.kubernetes.io/ssl-redirect forces HTTPS when TLS is configured. nginx.ingress.kubernetes.io/whitelist-source-range locks admin paths to office IPs. Store sensitive values in Kubernetes Secrets, not annotation plaintext. For Laravel apps, set APP_URL to the public HTTPS hostname the Ingress exposes.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: