Kubernetes Ingress and Exposing Services
Use a Service to give an app a stable address, and an Ingress to route traffic to it by hostname and path.
Once you've deployed an app on your server or VPS cluster, the next question is how anyone outside the cluster reaches it. Kubernetes offers several ways to expose a workload, and Ingress is the layer best suited to hosting multiple domains and sites behind one entry point. This article recaps the three Service types, then explains what Ingress does, what it needs to work, and gives you a YAML you can copy.
First, a recap: the three Service types
A Pod's IP is ephemeral and changes whenever it's recreated. A Service gives a group of Pods one stable address. There are three common type values:
- ClusterIP (the default): reachable only inside the cluster via an internal virtual IP. Great for service-to-service calls, invisible from outside.
- NodePort: opens a high port on every node (default range 30000-32767), reachable from outside as nodeIP:port. Simple, but the ports are awkward and it's not something you'd hand to end users.
- LoadBalancer: asks the underlying cloud or load balancer for an external IP. On a self-managed cluster you usually need an extra component (such as MetalLB) before a real external IP is assigned.
All three "expose a service," but they all stop at layer 4 (IP plus port). The moment you want to host several domains or sites on the same IP and the same port 443, you need layer-7 HTTP routing — and that is exactly what Ingress solves.
What Ingress is
An Ingress is a set of routing rules: based on the request's host and path, it forwards HTTP/HTTPS traffic to different Services. For example:
- app.example.com → web-service
- api.example.com/v1 → api-service
Multiple services then share one entry IP and one certificate, only 80/443 is exposed to the world, and each backend Service can stay a plain ClusterIP.
Prerequisite: you need an Ingress Controller
An Ingress object is just the rules. The thing that actually enforces them is an Ingress Controller — a long-running reverse proxy that reads those rules and applies them. Rules without a controller do nothing. Common choices:
- ingress-nginx: the most widely used community option, feature-rich, but you install it yourself.
- Traefik: lightweight, and bundled and enabled by default in k3s, so it works out of the box.
Check which one your cluster runs with the IngressClass or the controller Pods:
kubectl get ingressclass
kubectl get pods -A | grep -Ei 'ingress|traefik'
Writing an Ingress YAML
The manifest below routes two hosts/paths to two Services (assume they already exist as ClusterIP):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: site-ingress
namespace: default
spec:
ingressClassName: nginx # use "traefik" on a stock k3s cluster
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80
- host: api.example.com
http:
paths:
- path: /v1
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
A few things to get right: ingressClassName must match a controller that actually exists in the cluster; pathType is usually Prefix (prefix match) or Exact; and backend.service.port.number is the Service port, not the container port. After applying it, run kubectl get ingress to see the assigned address.
TLS certificates in brief
To serve HTTPS, first store the certificate and private key as a Secret of type kubernetes.io/tls:
kubectl create secret tls example-tls \
--cert=fullchain.pem --key=privkey.pem
Then add a tls block to the Ingress spec that ties the hosts to that Secret:
tls:
- hosts:
- app.example.com
- api.example.com
secretName: example-tls
The controller enables 443 for those hosts and terminates TLS, while the backend Services keep receiving plain HTTP. In production you'd typically let cert-manager issue and renew certificates automatically, so you never touch them by hand.
Summary
- A Service gives you a stable address: ClusterIP internal, NodePort a quick external port, LoadBalancer an external IP.
- An Ingress gives you layer-7 routing: fan traffic out to many Services by host and path, sharing one entry point and one certificate.
- For those rules to take effect, the cluster must run an Ingress Controller (install ingress-nginx yourself; k3s ships Traefik).
- When writing rules, match ingressClassName, pick the right pathType, use the Service port, and mount your certificate through a tls Secret for HTTPS.