Gendi Notes

Ingress and TLS

An Ingress object is a set of routing rules that does nothing on its own. A **controller** has to be installed to read it — and "my Ingress is ignored" is almost always that it is not.

Level 2 guide kubernetesingresstlsnetworking

What is it?#

An Ingress routes external HTTP(S) traffic to Services, by hostname and path, through one load balancer shared by every application.

internet │ ↓ ┌──────────────────────────────────┐ │ cloud LoadBalancer (ONE, shared)│ └──────────────────────────────────┘ │ ↓ ┌──────────────────────────────────┐ │ Ingress CONTROLLER pods │ nginx / Traefik / HAProxy │ · terminates TLS │ ← these do the actual work │ · matches host + path │ └──────────────────────────────────┘ │ ├── api.example.com ──→ Service: api ──→ pods ├── example.com/shop ──→ Service: shop ──→ pods └── example.com/ ──→ Service: web ──→ pods

Why it exists#

Without it, exposing ten services means ten LoadBalancer Services:

A LoadBalancer per ServiceOne Ingress
Cloud load balancers10 — and 10 bills1
TLS certificatesConfigured 10 timesManaged centrally
Path-based routingNot possible/api and / to different services
Adding a serviceProvision another LB, wait, update DNSOne more rule

How to use it#

Install a controller first#

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx --create-namespace

# It creates ONE LoadBalancer Service. This is the address DNS points at.
kubectl get svc -n ingress-nginx
kubectl get ingressclass

An Ingress with TLS#

ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: site
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod   # issues the cert
spec:
  ingressClassName: nginx        # MUST match an installed IngressClass
  tls:
    - hosts: [example.com, api.example.com]
      secretName: site-tls       # cert-manager CREATES this Secret
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: api
                port: { number: 80 }
    - host: example.com
      http:
        paths:
          - path: /shop
            pathType: Prefix
            backend:
              service: { name: shop, port: { number: 80 } }
          - path: /
            pathType: Prefix
            backend:
              service: { name: web, port: { number: 80 } }
FieldGotcha
ingressClassNameWrong value → silently ignored. Check kubectl get ingressclass
pathTypePrefix (usual), Exact, or ImplementationSpecific
tls.secretNameMust be in the same namespace as the Ingress
Path orderControllers match most specific first, not file order

Automatic certificates with cert-manager#

helm repo add jetstack https://charts.jetstack.io
helm install cert-manager jetstack/cert-manager \
  -n cert-manager --create-namespace --set crds.enabled=true
clusterissuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata: { name: letsencrypt-prod }
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: you@example.com
    privateKeySecretRef: { name: letsencrypt-prod-account-key }
    solvers:
      - http01:
          ingress: { ingressClassName: nginx }

Now the annotation on the Ingress is enough: cert-manager requests the certificate, proves control of the domain, writes the Secret, and renews it before expiry.

When a certificate does not appear, follow the chain
kubectl get certificate,certificaterequest,order,challenge -A
kubectl describe challenge <name>    # the ACME failure reason is here

What goes wrong#

SymptomCauseFix
Ingress created, nothing happensNo controller, or wrong ingressClassNamekubectl get ingressclass
ADDRESS column emptyController has no LoadBalancer, or the cloud cannot provision onekubectl get svc -n ingress-nginx
404 from the controllerNo rule matched the Host headerCheck the host spelling and DNS
502 / 503Backend Service has no endpointskubectl get endpoints <svc>
404 on a path that should workPath rewriting not configuredAdd the controller's rewrite annotation
TLS serves the default certificateThe tls Secret is missing or in another namespacekubectl get secret <name> -n <ingress-ns>
Certificate stuck FalseACME challenge failingkubectl describe challenge
Works on HTTP, redirect loop on HTTPSTLS terminated at the LB and the controller redirects againTrust X-Forwarded-Proto, or terminate in one place

Interview corner · الأسئلة اللي بتتسأل#

What is the difference between a LoadBalancer Service and an Ingress?

Layer 4 versus layer 7, and that difference produces all the others.

LoadBalancer ServiceIngress
Layer4 — TCP/UDP7 — HTTP/HTTPS
Can route by hostname or pathNoYes
TLS terminationAt the cloud LB, if configuredAt the controller
Cloud load balancers neededOne per ServiceOne, shared
Works for non-HTTPYesNo
Needs a controller installedNoYes

A Service cannot read a Host: header because it never parses the payload — it rewrites packet destinations. An Ingress controller is a reverse proxy that terminates the connection, reads the request, and opens a new one to the pod.

Which is why: Ingress is for HTTP fan-in, and LoadBalancer remains the answer for databases, gRPC where you want raw TCP, game servers, and anything not HTTP.

An Ingress returns 503. Walk me through it.

503 from an Ingress controller almost always means the controller has no healthy backend, not that the controller is broken. Work bottom-up:

# 1. Does the Service have endpoints? This answers it most of the time.
kubectl get endpoints <svc>          # <none> → stop here

# 2. If empty: selector mismatch, or pods not Ready?
kubectl get pods --show-labels
kubectl get svc <svc> -o jsonpath='{.spec.selector}{"\n"}'

# 3. If endpoints exist: does the Ingress point at the right Service and port?
kubectl describe ingress site

# 4. What does the controller itself say?
kubectl logs -n ingress-nginx deploy/ingress-nginx-controller --tail=50
FindingCause
ENDPOINTS: <none>Selector typo, or every pod failing readiness
Endpoints exist, still 503Ingress names a wrong Service or port
503 only during deploysEndpoint de-registration race — add a preStop sleep
503 for one host onlyThat rule's backend is broken; others are fine

Key takeaways#

  • An Ingress object does nothing without a controller. "It is ignored" is almost always a missing controller or a wrong ingressClassName.
  • Ingress is layer 7, so it can route by host and path; a Service cannot.
  • One load balancer, many services — that is the cost saving.
  • 503 means no healthy endpoints. Check kubectl get endpoints first.
  • The TLS Secret must be in the Ingress's namespace.
  • HTTP-01 needs port 80 reachable; use DNS-01 for wildcards.
  • Gateway API is the successor — worth knowing as Ingress is feature-frozen.

Last updated 2026-09-13