Kubernetes Ingress and TLS: Exposing Services Properly
Kubernetes Ingress and TLS: Exposing Services Properly
Services give your Pods stable networking inside the cluster. Ingress is how the outside world reaches them — HTTP/S routing, hostnames, paths, and TLS, all in one Kubernetes-native object.
Table of Contents
- The mental model
- Your first Ingress
- Path and host routing
- TLS: free certificates with cert-manager
- Debugging when it doesn't work
- Ingress vs Gateway API
The mental model
An Ingress resource is just a rulebook. Something has to enforce it — the Ingress Controller (NGINX Ingress, Traefik, HAProxy, cloud LB controllers). Without a controller, Ingress objects do nothing.
kind cluster + ingress-nginx:
helm install ingress-nginx ingress-nginx/ingress-nginx \
-n ingress-nginx --create-namespace
The controller spins up an nginx deployment watching Ingress objects, and exposes itself via a LoadBalancer/NodePort service.
Your first Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
Traffic flow: client → controller → Service web:80 → healthy Pods.
Path and host routing
rules:
- host: api.example.com # virtual host routing
http:
paths:
- path: /
pathType: Prefix
backend: { service: { name: api, port: { number: 8080 } } }
- host: app.example.com
http:
paths:
- path: /app # path routing
pathType: Prefix
backend: { service: { name: web, port: { number: 80 } } }
- path: /static
pathType: Prefix
backend: { service: { name: cdn-cache, port: { number: 80 } } }
Prefix vs Exact matching matters — /app with Prefix also catches /apples. Use /app/ carefully.
TLS: free certificates with cert-manager
cert-manager automates Let's Encrypt inside Kubernetes:
helm install cert-manager jetstack/cert-manager \
-n cert-manager --create-namespace \
--set crds.enabled=true
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: you@example.com
privateKeySecretRef: { name: letsencrypt-key }
solvers:
- http01: { ingress: { class: nginx } }
Then one annotation on your Ingress provisions and renews certs:
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt
spec:
tls:
- hosts: [app.example.com]
secretName: app-tls # cert-manager fills this
Debugging when it doesn't work
kubectl get ingress # ADDRESS empty? controller isn't routing yet
kubectl describe ingress web # events show misconfigurations
kubectl logs -n ingress-nginx deploy/ingress-nginx-controller
kubectl get certificate # cert-manager status
Top three causes of "Ingress does nothing": missing ingressClassName, the Service name/port doesn't match, or DNS not pointing at the controller's address.
Ingress vs Gateway API
Gateway API is the successor — richer (TCP/UDP, traffic splitting, role separation between platform and app teams). Ingress isn't going anywhere soon, but for new builds on modern clusters, Gateway API is worth the look.
Related Articles
- Kubernetes for Beginners: Pods, Deployments, and Services
- The Complete kubectl Cheat Sheet: Every Command That Matters
Last Updated: October 2026 Author: CloudOpsGuide Team Difficulty: Intermediate Estimated Reading Time: 11 minutes