Skip to content

Expose a service with Ingress

If your cluster was provisioned with the optional ingress bundle, it already runs Traefik (the default traefik IngressClass) and cert-manager with a letsencrypt-prod ClusterIssuer.

This guide uses the Ingress API, which is the simplest path. For more control (header routing, traffic splitting, multiple backends) see Expose a service with the Gateway API.

We start over plain HTTP to confirm routing works, then add a domain and HTTPS by overwriting the same Ingress.

Terminal window
cat <<'EOF' | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-ingress
spec:
replicas: 1
selector:
matchLabels:
app: hello-ingress
template:
metadata:
labels:
app: hello-ingress
spec:
containers:
- name: hello-ingress
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: hello-ingress
spec:
selector:
app: hello-ingress
ports:
- port: 80
targetPort: 80
EOF

Create an Ingress. This rule matches any request, so Traefik serves it to anyone hitting the node:

Terminal window
cat <<'EOF' | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hello-ingress
spec:
ingressClassName: traefik
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hello-ingress
port:
number: 80
EOF

Reach it directly at your node IP (the same IP as your API server, https://<your-node-ip>:6443):

Terminal window
curl http://<your-node-ip>

You should get the nginx welcome page.

To serve real traffic over HTTPS you need a hostname that resolves to the cluster. Certificates are issued by Let’s Encrypt over an HTTP-01 challenge, so DNS must resolve before you apply.

In your DNS provider, create an A record pointing your hostname at your cluster’s node IP:

ingress.example.com. A <your-node-ip>

Confirm it resolves before continuing:

Terminal window
dig +short ingress.example.com

Now overwrite the Ingress with your real hostname, the cert-manager.io/cluster-issuer annotation, and a tls block. cert-manager issues a certificate into the hello-ingress-tls secret; Traefik then serves it.

Terminal window
cat <<'EOF' | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hello-ingress
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: traefik
tls:
- hosts:
- ingress.example.com
secretName: hello-ingress-tls
rules:
- host: ingress.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hello-ingress
port:
number: 80
EOF

The certificate takes a minute or two on first issuance. Watch it become ready:

Terminal window
kubectl get certificate hello-ingress-tls -w

Once READY is True, your app is live over HTTPS:

Terminal window
curl https://ingress.example.com

If issuance stalls, describe the certificate to see the challenge status:

Terminal window
kubectl describe certificate hello-ingress-tls

The usual cause is DNS that doesn’t yet resolve to your node IP.

Terminal window
kubectl delete ingress hello-ingress
kubectl delete service hello-ingress
kubectl delete deployment hello-ingress
kubectl delete secret hello-ingress-tls --ignore-not-found