Ingress and Networking
Ingress resources routing to multiple Services, TLS termination, and Network Policies briefly.
Why Services alone aren't enough for HTTP routing
The Services page covered LoadBalancer as the way to expose a Service to the public internet — but a LoadBalancer Service provisions one real external load balancer per Service, gives you no control over path- or host-based routing, and has no built-in concept of TLS at all. A typical application needs several distinct HTTP routes (an API, a web frontend, an admin panel) reachable under one domain, often each backed by a different Service — provisioning a separate cloud load balancer per route is both expensive and the wrong tool for what is fundamentally an HTTP routing problem, not a networking one. Ingress is Kubernetes' answer: a single resource that describes HTTP(S) routing rules across multiple Services, backed by one shared entry point.
Ingress Controllers and Ingress resources
An Ingress resource just declares routing rules — it does nothing on its own. Making those rules actually work requires an Ingress Controller running in the cluster (commonly ingress-nginx, or a cloud provider's own controller) that watches for Ingress resources and configures a real reverse proxy accordingly. This mirrors the same declarative pattern the rest of Kubernetes uses: you describe desired routing state, and a controller reconciles it into an actual running configuration — install an Ingress Controller once per cluster, then define as many Ingress resources as you have routing needs.
A complete routing example
Here's an Ingress routing two different hostnames to two different Services, plus a path-based split on one of them:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend-service
port:
number: 80
- path: /api/(.*)
pathType: ImplementationSpecific
backend:
service:
name: api-service
port:
number: 8080
- host: admin.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: admin-service
port:
number: 80
ingressClassName: nginx— tells Kubernetes which installed Ingress Controller should handle this resource, relevant when a cluster runs more than one.rules[].host— routes based on theHostheader, the same way an Nginxserver_nameblock does (see the Nginx track for the underlying concept) —app.example.comandadmin.example.comare handled by entirely separate rule blocks here, each able to route to different Services.paths[].path+pathType— routes based on the URL path within a matched host.Prefixmatches anything starting with that path; here,/api/(.*)onapp.example.comsends anything under/api/toapi-service, while everything else on that host falls through tofrontend-service.backend.service.name/port.number— the existing Service (defined exactly like the Services covered in the earlier Pods/Deployments/Services page) that requests matching this rule get forwarded to. Ingress routes to Services, never directly to Pods.
kubectl apply -f ingress.yaml
kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
my-app-ingress nginx app.example.com,admin.example.com 34.120.11.45 80 2m
One shared external address now fronts every route defined across both hostnames — a single load balancer doing the job that would otherwise take one LoadBalancer Service per backend.
TLS on an Ingress
Ingress can also terminate HTTPS directly, referencing a Secret holding a TLS certificate and key (the same kind of Secret covered on the ConfigMaps/Secrets page, just with a reserved kubernetes.io/tls type):
spec:
tls:
- hosts:
- app.example.com
secretName: app-example-com-tls
rules:
- host: app.example.com
# ... same rules as above
In practice, most clusters automate certificate issuance and renewal for this with cert-manager, an add-on that watches Ingress resources and requests/renews certificates (commonly from Let's Encrypt) automatically rather than requiring the certificate Secret to be created and rotated by hand.
Network Policies, briefly
By default, every Pod in a cluster can send traffic to every other Pod, with nothing restricting it — an Ingress and Services control how traffic gets into the cluster and to a Service, but say nothing about which Pods are allowed to talk to which other Pods once inside. A NetworkPolicy closes that gap by explicitly allowing only specified traffic, denying everything else to a selected set of Pods:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-api-only
spec:
podSelector:
matchLabels:
app: api-service
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend-service
ports:
- protocol: TCP
port: 8080
This policy targets Pods labeled app: api-service and allows inbound traffic only from Pods labeled app: frontend-service, on port 8080 — any other Pod in the cluster (including, notably, a compromised or misconfigured one) is denied by default the moment any NetworkPolicy selects that Pod at all. NetworkPolicies require a compatible CNI network plugin to actually enforce them (not every cluster networking setup does); applying one on a cluster whose network plugin doesn't support NetworkPolicies has no effect and silently leaves traffic unrestricted.
Common mistakes
- Installing Ingress resources with no Ingress Controller running in the cluster — the resource is accepted by the API server but nothing actually implements the routing;
kubectl get ingressshows noADDRESSat all. - Forgetting
ingressClassNamein a cluster running multiple Ingress Controllers — the resource can sit unclaimed, matched by none of them. - Assuming Ingress replaces Services entirely — Ingress routes HTTP(S) to existing Services, it doesn't replace the need to define them.
- Writing a NetworkPolicy that only restricts ingress traffic to a sensitive Pod (like a database) while leaving every other Pod's egress completely unrestricted — a policy is only as protective as every relevant direction it actually covers.