Home / Ingress step by step
How to expose an app in Kubernetes with Ingress, step by step
From a Pod that only talks inside the cluster to a public domain with HTTPS.
By default, a Kubernetes Pod is only reachable inside the cluster. For a user on the internet to reach your app you need to chain three pieces together: the Pod, a Service giving it a stable address, and an Ingress routing external traffic to that Service by domain and path.
Step 1 — the Service (a stable internal address)
Pods are created and destroyed constantly (when scaling, when updating the image), so their IPs change. A Service gives them a name and an internal IP that never change, and spreads traffic across every Pod matching its selector:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: ClusterIP
selector:
app: my-app
ports:
- port: 80
targetPort: 80
protocol: TCP
type: ClusterIP (the default) is enough here — the Ingress talks to this Service from inside the cluster, so it doesn't need to be reachable from outside on its own.
Step 2 — the Ingress Controller (install once)
An Ingress object on its own does nothing: it needs an Ingress Controller running in the cluster to read those objects and configure a real proxy (nginx, Traefik, HAProxy…). If your cluster is managed (GKE, EKS, AKS) it may already have one, or you install it with a single command (for example ingress-nginx via Helm). This is done once per cluster, not per app.
Step 3 — the Ingress object (the routing rules)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
spec:
ingressClassName: nginx
tls:
- hosts:
- app.mydomain.com
secretName: my-tls-secret
rules:
- host: app.mydomain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-service
port:
number: 80
ingressClassName tells Kubernetes which Ingress Controller should handle this rule (useful when you run more than one). Each entry under rules associates a host with a list of paths; each path points at a specific Service and port — so you can route /api to a backend and / to a frontend with the same Ingress.
Step 4 — TLS (the padlock in the browser)
The tls block tells the Ingress Controller to serve that host over HTTPS using the certificate stored in the Secret my-tls-secret (of type kubernetes.io/tls, with the keys tls.crt and tls.key). In production that Secret is almost always filled in automatically by cert-manager, requesting a free certificate from Let's Encrypt — but the Ingress doesn't need to know how it was generated, only where it is.
What if I only have one app and don't want an Ingress Controller?
For a very simple case, a Service of type LoadBalancer (on a cloud that supports it) gives you a public IP directly, with no Ingress needed. The downside is that each Service of that type usually bills its own cloud load balancer — with two or three apps, a single Ingress works out cheaper and tidier.
Generate the Service and the Ingress for your app — TLS block included — with our Kubernetes manifest generator, and combine both into a single file with the bundle mode.